Merge "Updating documentation for setOnClickPendingIntent"
diff --git a/Android.mk b/Android.mk
index 6186c55..75e9a24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -618,6 +618,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES :=                           \
     framework-protos                                     \
     android.hidl.base-V1.0-java                          \
+    android.hardware.health-V1.0-java                    \
+    android.hardware.health-V2.0-java                    \
     android.hardware.cas-V1.0-java                       \
     android.hardware.health-V1.0-java-constants          \
     android.hardware.thermal-V1.0-java-constants         \
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 47dd257..586c385 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -46,7 +46,7 @@
     private static final float SPACING_ADD = 10f;
     private static final float SPACING_MULT = 1.5f;
 
-    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
index 34de65d..9d11f29 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -40,7 +40,7 @@
 
     private static final boolean[] BOOLEANS = new boolean[]{false, true};
 
-    @Parameterized.Parameters(name = "cached={4},{1} chars,{0}")
+    @Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index 00b60ad..6768798 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -42,7 +42,7 @@
 
     private static final boolean[] BOOLEANS = new boolean[]{false, true};
 
-    @Parameterized.Parameters(name = "cached={1},{0} chars")
+    @Parameterized.Parameters(name = "cached={1},{0}chars")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index 356e2e0..bfdb758 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -50,7 +50,7 @@
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index a2bf33e1..ff2d57e 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -40,7 +40,7 @@
 import java.util.Random;
 
 /**
- * Performance test for multi line, single style {@link StaticLayout} creation/draw.
+ * Performance test for {@link TextView} measure/draw.
  */
 @LargeTest
 @RunWith(Parameterized.class)
@@ -51,7 +51,7 @@
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/api/current.txt b/api/current.txt
index f133562..93cc843 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -362,6 +362,7 @@
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
     field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+    field public static final int cantSaveState = 16844142; // 0x101056e
     field public static final deprecated int capitalize = 16843113; // 0x1010169
     field public static final int category = 16843752; // 0x10103e8
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -5643,8 +5644,10 @@
     method public static java.lang.String suppressedEffectsToString(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
+    field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20
     field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
     field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
+    field public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 64; // 0x40
     field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
     field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1
     field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10
@@ -10999,6 +11002,7 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
+    method public int getDisabledReason();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
@@ -11017,6 +11021,13 @@
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int DISABLED_REASON_APP_CHANGED = 2; // 0x2
+    field public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101; // 0x65
+    field public static final int DISABLED_REASON_BY_APP = 1; // 0x1
+    field public static final int DISABLED_REASON_NOT_DISABLED = 0; // 0x0
+    field public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103; // 0x67
+    field public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102; // 0x66
+    field public static final int DISABLED_REASON_VERSION_LOWER = 100; // 0x64
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -31834,6 +31845,7 @@
     field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final java.lang.String DISALLOW_SMS = "no_sms";
+    field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -40033,8 +40045,12 @@
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
     field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
     field public static final int RESULT_CANCELLED = 2; // 0x2
+    field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
     field public static final int RESULT_EXPIRED = 3; // 0x3
+    field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
     field public static final int RESULT_IO_ERROR = 4; // 0x4
+    field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+    field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
     field public static final int RESULT_SUCCESSFUL = 1; // 0x1
     field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
     field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -40701,11 +40717,10 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
@@ -45745,8 +45760,8 @@
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
     method public void bringToFront();
-    method public void buildDrawingCache();
-    method public void buildDrawingCache(boolean);
+    method public deprecated void buildDrawingCache();
+    method public deprecated void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
     method public boolean canResolveLayoutDirection();
@@ -45771,7 +45786,7 @@
     method protected int computeVerticalScrollRange();
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
-    method public void destroyDrawingCache();
+    method public deprecated 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);
@@ -45852,10 +45867,10 @@
     method public static int getDefaultSize(int, int);
     method public android.view.Display getDisplay();
     method public final int[] getDrawableState();
-    method public android.graphics.Bitmap getDrawingCache();
-    method public android.graphics.Bitmap getDrawingCache(boolean);
-    method public int getDrawingCacheBackgroundColor();
-    method public int getDrawingCacheQuality();
+    method public deprecated android.graphics.Bitmap getDrawingCache();
+    method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+    method public deprecated int getDrawingCacheBackgroundColor();
+    method public deprecated int getDrawingCacheQuality();
     method public void getDrawingRect(android.graphics.Rect);
     method public long getDrawingTime();
     method public float getElevation();
@@ -45994,7 +46009,7 @@
     method public boolean isClickable();
     method public boolean isContextClickable();
     method public boolean isDirty();
-    method public boolean isDrawingCacheEnabled();
+    method public deprecated boolean isDrawingCacheEnabled();
     method public boolean isDuplicateParentStateEnabled();
     method public boolean isEnabled();
     method public final boolean isFocusable();
@@ -46165,9 +46180,9 @@
     method public void setContentDescription(java.lang.CharSequence);
     method public void setContextClickable(boolean);
     method public void setDefaultFocusHighlightEnabled(boolean);
-    method public void setDrawingCacheBackgroundColor(int);
-    method public void setDrawingCacheEnabled(boolean);
-    method public void setDrawingCacheQuality(int);
+    method public deprecated void setDrawingCacheBackgroundColor(int);
+    method public deprecated void setDrawingCacheEnabled(boolean);
+    method public deprecated void setDrawingCacheQuality(int);
     method public void setDuplicateParentStateEnabled(boolean);
     method public void setElevation(float);
     method public void setEnabled(boolean);
@@ -46318,9 +46333,9 @@
     field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
-    field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
-    field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
-    field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+    field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
     field protected static final int[] EMPTY_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -46712,7 +46727,7 @@
     method public android.animation.LayoutTransition getLayoutTransition();
     method public int getNestedScrollAxes();
     method public android.view.ViewGroupOverlay getOverlay();
-    method public int getPersistentDrawingCache();
+    method public deprecated int getPersistentDrawingCache();
     method public boolean getTouchscreenBlocksFocus();
     method public int indexOfChild(android.view.View);
     method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -46765,7 +46780,7 @@
     method public void setAddStatesFromChildren(boolean);
     method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
     method public deprecated void setAnimationCacheEnabled(boolean);
-    method protected void setChildrenDrawingCacheEnabled(boolean);
+    method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
     method protected void setChildrenDrawingOrderEnabled(boolean);
     method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
     method public void setClipChildren(boolean);
@@ -46777,7 +46792,7 @@
     method public void setLayoutTransition(android.animation.LayoutTransition);
     method public void setMotionEventSplittingEnabled(boolean);
     method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
-    method public void setPersistentDrawingCache(int);
+    method public deprecated void setPersistentDrawingCache(int);
     method protected void setStaticTransformationsEnabled(boolean);
     method public void setTouchscreenBlocksFocus(boolean);
     method public void setTransitionGroup(boolean);
@@ -46795,10 +46810,10 @@
     field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
     field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
     field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
-    field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
-    field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
-    field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
-    field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+    field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+    field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+    field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+    field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
   }
 
   public static class ViewGroup.LayoutParams {
diff --git a/api/system-current.txt b/api/system-current.txt
index c1b109c..9767e44 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -494,6 +494,7 @@
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
     field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+    field public static final int cantSaveState = 16844142; // 0x101056e
     field public static final deprecated int capitalize = 16843113; // 0x1010169
     field public static final int category = 16843752; // 0x10103e8
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -5851,8 +5852,10 @@
     method public static java.lang.String suppressedEffectsToString(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
+    field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20
     field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
     field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
+    field public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 64; // 0x40
     field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
     field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1
     field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10
@@ -11722,6 +11725,7 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
+    method public int getDisabledReason();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
@@ -11740,6 +11744,13 @@
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int DISABLED_REASON_APP_CHANGED = 2; // 0x2
+    field public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101; // 0x65
+    field public static final int DISABLED_REASON_BY_APP = 1; // 0x1
+    field public static final int DISABLED_REASON_NOT_DISABLED = 0; // 0x0
+    field public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103; // 0x67
+    field public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102; // 0x66
+    field public static final int DISABLED_REASON_VERSION_LOWER = 100; // 0x64
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -27217,7 +27228,6 @@
   }
 
   public static final class TvInputManager.Hardware {
-    method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
     method public void overrideAudioSink(int, java.lang.String, int, int, int);
     method public void setStreamVolume(float);
     method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
@@ -34678,6 +34688,7 @@
     field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final java.lang.String DISALLOW_SMS = "no_sms";
+    field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -43543,8 +43554,12 @@
     field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
     field public static final int RESULT_CANCELLED = 2; // 0x2
+    field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
     field public static final int RESULT_EXPIRED = 3; // 0x3
+    field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
     field public static final int RESULT_IO_ERROR = 4; // 0x4
+    field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+    field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
     field public static final int RESULT_SUCCESSFUL = 1; // 0x1
     field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
     field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -44330,13 +44345,12 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
@@ -49471,8 +49485,8 @@
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
     method public void bringToFront();
-    method public void buildDrawingCache();
-    method public void buildDrawingCache(boolean);
+    method public deprecated void buildDrawingCache();
+    method public deprecated void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
     method public boolean canResolveLayoutDirection();
@@ -49497,7 +49511,7 @@
     method protected int computeVerticalScrollRange();
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
-    method public void destroyDrawingCache();
+    method public deprecated 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);
@@ -49578,10 +49592,10 @@
     method public static int getDefaultSize(int, int);
     method public android.view.Display getDisplay();
     method public final int[] getDrawableState();
-    method public android.graphics.Bitmap getDrawingCache();
-    method public android.graphics.Bitmap getDrawingCache(boolean);
-    method public int getDrawingCacheBackgroundColor();
-    method public int getDrawingCacheQuality();
+    method public deprecated android.graphics.Bitmap getDrawingCache();
+    method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+    method public deprecated int getDrawingCacheBackgroundColor();
+    method public deprecated int getDrawingCacheQuality();
     method public void getDrawingRect(android.graphics.Rect);
     method public long getDrawingTime();
     method public float getElevation();
@@ -49720,7 +49734,7 @@
     method public boolean isClickable();
     method public boolean isContextClickable();
     method public boolean isDirty();
-    method public boolean isDrawingCacheEnabled();
+    method public deprecated boolean isDrawingCacheEnabled();
     method public boolean isDuplicateParentStateEnabled();
     method public boolean isEnabled();
     method public final boolean isFocusable();
@@ -49891,9 +49905,9 @@
     method public void setContentDescription(java.lang.CharSequence);
     method public void setContextClickable(boolean);
     method public void setDefaultFocusHighlightEnabled(boolean);
-    method public void setDrawingCacheBackgroundColor(int);
-    method public void setDrawingCacheEnabled(boolean);
-    method public void setDrawingCacheQuality(int);
+    method public deprecated void setDrawingCacheBackgroundColor(int);
+    method public deprecated void setDrawingCacheEnabled(boolean);
+    method public deprecated void setDrawingCacheQuality(int);
     method public void setDuplicateParentStateEnabled(boolean);
     method public void setElevation(float);
     method public void setEnabled(boolean);
@@ -50044,9 +50058,9 @@
     field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
-    field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
-    field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
-    field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+    field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
     field protected static final int[] EMPTY_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -50438,7 +50452,7 @@
     method public android.animation.LayoutTransition getLayoutTransition();
     method public int getNestedScrollAxes();
     method public android.view.ViewGroupOverlay getOverlay();
-    method public int getPersistentDrawingCache();
+    method public deprecated int getPersistentDrawingCache();
     method public boolean getTouchscreenBlocksFocus();
     method public int indexOfChild(android.view.View);
     method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -50491,7 +50505,7 @@
     method public void setAddStatesFromChildren(boolean);
     method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
     method public deprecated void setAnimationCacheEnabled(boolean);
-    method protected void setChildrenDrawingCacheEnabled(boolean);
+    method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
     method protected void setChildrenDrawingOrderEnabled(boolean);
     method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
     method public void setClipChildren(boolean);
@@ -50503,7 +50517,7 @@
     method public void setLayoutTransition(android.animation.LayoutTransition);
     method public void setMotionEventSplittingEnabled(boolean);
     method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
-    method public void setPersistentDrawingCache(int);
+    method public deprecated void setPersistentDrawingCache(int);
     method protected void setStaticTransformationsEnabled(boolean);
     method public void setTouchscreenBlocksFocus(boolean);
     method public void setTransitionGroup(boolean);
@@ -50521,10 +50535,10 @@
     field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
     field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
     field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
-    field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
-    field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
-    field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
-    field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+    field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+    field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+    field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+    field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
   }
 
   public static class ViewGroup.LayoutParams {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7ee261e..639877f 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -275,6 +275,10 @@
     method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
   }
 
+  public static final class TvInputManager.Hardware {
+    method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
+  }
+
   public class TvView extends android.view.ViewGroup {
     method public void requestUnblockContent(android.media.tv.TvContentRating);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 91b47d5..702ec3b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -362,6 +362,7 @@
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
     field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+    field public static final int cantSaveState = 16844142; // 0x101056e
     field public static final deprecated int capitalize = 16843113; // 0x1010169
     field public static final int category = 16843752; // 0x10103e8
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -5669,8 +5670,10 @@
     method public static java.lang.String suppressedEffectsToString(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
+    field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20
     field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
     field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
+    field public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 64; // 0x40
     field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
     field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1
     field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10
@@ -11080,6 +11083,7 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
+    method public int getDisabledReason();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
@@ -11096,8 +11100,16 @@
     method public boolean isEnabled();
     method public boolean isImmutable();
     method public boolean isPinned();
+    method public boolean isVisibleToPublisher();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int DISABLED_REASON_APP_CHANGED = 2; // 0x2
+    field public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101; // 0x65
+    field public static final int DISABLED_REASON_BY_APP = 1; // 0x1
+    field public static final int DISABLED_REASON_NOT_DISABLED = 0; // 0x0
+    field public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103; // 0x67
+    field public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102; // 0x66
+    field public static final int DISABLED_REASON_VERSION_LOWER = 100; // 0x64
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -32097,6 +32109,7 @@
     field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final java.lang.String DISALLOW_SMS = "no_sms";
+    field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -40414,8 +40427,12 @@
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
     field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
     field public static final int RESULT_CANCELLED = 2; // 0x2
+    field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
     field public static final int RESULT_EXPIRED = 3; // 0x3
+    field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
     field public static final int RESULT_IO_ERROR = 4; // 0x4
+    field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+    field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
     field public static final int RESULT_SUCCESSFUL = 1; // 0x1
     field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
     field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -41082,11 +41099,10 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
@@ -46315,8 +46331,8 @@
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
     method public void bringToFront();
-    method public void buildDrawingCache();
-    method public void buildDrawingCache(boolean);
+    method public deprecated void buildDrawingCache();
+    method public deprecated void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
     method public boolean canResolveLayoutDirection();
@@ -46341,7 +46357,7 @@
     method protected int computeVerticalScrollRange();
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
-    method public void destroyDrawingCache();
+    method public deprecated 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);
@@ -46422,10 +46438,10 @@
     method public static int getDefaultSize(int, int);
     method public android.view.Display getDisplay();
     method public final int[] getDrawableState();
-    method public android.graphics.Bitmap getDrawingCache();
-    method public android.graphics.Bitmap getDrawingCache(boolean);
-    method public int getDrawingCacheBackgroundColor();
-    method public int getDrawingCacheQuality();
+    method public deprecated android.graphics.Bitmap getDrawingCache();
+    method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+    method public deprecated int getDrawingCacheBackgroundColor();
+    method public deprecated int getDrawingCacheQuality();
     method public void getDrawingRect(android.graphics.Rect);
     method public long getDrawingTime();
     method public float getElevation();
@@ -46566,7 +46582,7 @@
     method public boolean isContextClickable();
     method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public boolean isDirty();
-    method public boolean isDrawingCacheEnabled();
+    method public deprecated boolean isDrawingCacheEnabled();
     method public boolean isDuplicateParentStateEnabled();
     method public boolean isEnabled();
     method public final boolean isFocusable();
@@ -46740,9 +46756,9 @@
     method public void setContentDescription(java.lang.CharSequence);
     method public void setContextClickable(boolean);
     method public void setDefaultFocusHighlightEnabled(boolean);
-    method public void setDrawingCacheBackgroundColor(int);
-    method public void setDrawingCacheEnabled(boolean);
-    method public void setDrawingCacheQuality(int);
+    method public deprecated void setDrawingCacheBackgroundColor(int);
+    method public deprecated void setDrawingCacheEnabled(boolean);
+    method public deprecated void setDrawingCacheQuality(int);
     method public void setDuplicateParentStateEnabled(boolean);
     method public void setElevation(float);
     method public void setEnabled(boolean);
@@ -46894,9 +46910,9 @@
     field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
-    field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
-    field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
-    field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+    field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+    field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
     field protected static final int[] EMPTY_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -47292,7 +47308,7 @@
     method public android.animation.LayoutTransition getLayoutTransition();
     method public int getNestedScrollAxes();
     method public android.view.ViewGroupOverlay getOverlay();
-    method public int getPersistentDrawingCache();
+    method public deprecated int getPersistentDrawingCache();
     method public boolean getTouchscreenBlocksFocus();
     method public int indexOfChild(android.view.View);
     method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -47345,7 +47361,7 @@
     method public void setAddStatesFromChildren(boolean);
     method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
     method public deprecated void setAnimationCacheEnabled(boolean);
-    method protected void setChildrenDrawingCacheEnabled(boolean);
+    method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
     method protected void setChildrenDrawingOrderEnabled(boolean);
     method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
     method public void setClipChildren(boolean);
@@ -47357,7 +47373,7 @@
     method public void setLayoutTransition(android.animation.LayoutTransition);
     method public void setMotionEventSplittingEnabled(boolean);
     method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
-    method public void setPersistentDrawingCache(int);
+    method public deprecated void setPersistentDrawingCache(int);
     method protected void setStaticTransformationsEnabled(boolean);
     method public void setTouchscreenBlocksFocus(boolean);
     method public void setTransitionGroup(boolean);
@@ -47375,10 +47391,10 @@
     field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
     field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
     field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
-    field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
-    field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
-    field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
-    field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+    field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+    field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+    field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+    field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
   }
 
   public static class ViewGroup.LayoutParams {
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index b16188e..e5d35b3 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -34,13 +34,6 @@
     iot/BootAction.cpp \
     iot/BootParameters.cpp \
 
-LOCAL_SHARED_LIBRARIES += \
-    libandroidthings \
-    libbase \
-    libbinder \
-
-LOCAL_STATIC_LIBRARIES += cpufeatures
-
 else
 
 LOCAL_SRC_FILES += \
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 6526123..d1af71d 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -260,9 +260,9 @@
     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
             dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
 
-    SurfaceComposerClient::openGlobalTransaction();
-    control->setLayer(0x40000000);
-    SurfaceComposerClient::closeGlobalTransaction();
+    SurfaceComposerClient::Transaction t;
+    t.setLayer(control, 0x40000000)
+        .apply();
 
     sp<Surface> s = control->getSurface();
 
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 742f9c24..00cef43 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "IotBootAnimation"
 
-#include <android-base/file.h>
+#include <base/files/file_util.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
@@ -31,13 +31,14 @@
 #include "BootParameters.h"
 
 using namespace android;
-using android::base::ReadFileToString;
 
 // Create a typedef for readability.
 typedef android::BootAnimation::Animation Animation;
 
 namespace {
 
+constexpr const char* kDefaultLibName = "libbootaction.so";
+
 class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
 public:
     BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
@@ -49,11 +50,13 @@
         // This value is optionally provided by the user and will be written to
         // /oem/oem.prop.
         char property[PROP_VALUE_MAX] = {0};
-        if (property_get("ro.oem.bootactions.lib", property, "") < 1) {
-            ALOGI("No bootaction specified");
+        property_get("ro.oem.bootactions.lib", property, kDefaultLibName);
+        library_path += property;
+
+        if (!::base::PathExists(::base::FilePath(library_path))) {
+            ALOGI("Skipping boot actions: %s does not exist", library_path.c_str());
             return;
         }
-        library_path += property;
 
         mBootAction = new BootAction();
         if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 07a064cf..37f6ed7 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -33,18 +33,18 @@
 {
     EncodedBuffer::Pointer snapshot = iter->rp()->copy();
     size_t bytesToWrite = 0;
-    uint32_t varint = 0;
+    uint64_t varint = 0;
     switch (wireType) {
         case WIRE_TYPE_VARINT:
             varint = iter->readRawVarint();
-            if(!skip) return buf->writeRawVarint(varint);
+            if(!skip) return buf->writeRawVarint64(varint);
             break;
         case WIRE_TYPE_FIXED64:
             bytesToWrite = 8;
             break;
         case WIRE_TYPE_LENGTH_DELIMITED:
             bytesToWrite = iter->readRawVarint();
-            if(!skip) buf->writeRawVarint(bytesToWrite);
+            if(!skip) buf->writeRawVarint32(bytesToWrite);
             break;
         case WIRE_TYPE_FIXED32:
             bytesToWrite = 4;
@@ -76,7 +76,6 @@
     uint8_t wireType = read_wire_type(varint);
     uint32_t fieldId = read_field_id(varint);
     const Privacy* policy = parentPolicy->lookup(fieldId);
-
     if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
         bool skip = !spec.CheckPremission(policy);
         size_t amt = buf->size();
@@ -99,7 +98,7 @@
     }
 
     buf->writeHeader(fieldId, wireType);
-    buf->writeRawVarint(finalSize);
+    buf->writeRawVarint32(finalSize);
     while (!q.empty()) {
         EncodedBuffer* subField = q.front();
         EncodedBuffer::iterator it = subField->begin();
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index ca36d27..4c95007 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,24 +42,7 @@
 LOCAL_SRC_FILES := \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
-    src/StatsService.cpp \
-    src/AnomalyMonitor.cpp \
-    src/LogEntryPrinter.cpp \
-    src/LogReader.cpp \
-    src/main.cpp \
-    src/DropboxWriter.cpp \
-    src/parse_util.cpp \
-    src/StatsLogProcessor.cpp \
-    src/stats_log.proto \
-    src/statsd_config.proto \
-    src/StatsPullerManager.cpp \
-    src/KernelWakelockPuller.cpp \
-    src/DropboxReader.cpp \
-    src/matchers/LogEntryMatcherManager.cpp \
-    src/metrics/CountMetricProducer.cpp \
-    src/metrics/ConditionTracker.cpp \
-    src/metrics/MetricsManager.cpp \
-    src/metrics/CountAnomalyTracker.cpp \
+    $(call all-cpp-files-under,src) \
 
 LOCAL_CFLAGS += \
     -Wall \
@@ -129,13 +112,21 @@
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
-    tests/indexed_priority_queue_test.cpp \
-    src/parse_util.cpp \
+    src/AnomalyMonitor.cpp \
+    src/stats_util.cpp \
     src/LogEntryPrinter.cpp \
     src/LogReader.cpp \
-    src/matchers/LogEntryMatcherManager.cpp \
-    tests/LogReader_test.cpp \
-    tests/LogEntryMatcher_test.cpp \
+    src/matchers/matcher_util.cpp \
+    src/condition/SimpleConditionTracker.cpp \
+    src/condition/CombinationConditionTracker.cpp \
+    src/matchers/SimpleLogMatchingTracker.cpp \
+    src/matchers/CombinationLogMatchingTracker.cpp \
+    src/metrics/metrics_manager_util.cpp \
+    src/metrics/CountMetricProducer.cpp \
+    src/metrics/CountAnomalyTracker.cpp \
+    src/condition/condition_util.cpp \
+    src/UidMap.cpp \
+    $(call all-cpp-files-under, tests) \
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp
index 92fe844..4fbbc7a 100644
--- a/cmds/statsd/src/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/AnomalyMonitor.cpp
@@ -90,6 +90,36 @@
     }
 }
 
+// More efficient than repeatedly calling remove(mPq.top()) since it batches the
+// updates to the registered alarm.
+unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>
+                AnomalyMonitor::popSoonerThan(uint32_t timestampSec) {
+
+    if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec);
+    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
+    std::lock_guard<std::mutex> lock(mLock);
+
+    for (sp<const AnomalyAlarm> t = mPq.top();
+                t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) {
+        oldAlarms.insert(t);
+        mPq.pop(); // remove t
+    }
+    // Always update registered alarm time (if anything has changed).
+    if (!oldAlarms.empty()) {
+        if (mPq.empty()) {
+            if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+            mRegisteredAlarmTimeSec = 0;
+            if (mStatsCompanionService != nullptr) {
+                mStatsCompanionService->cancelAnomalyAlarm();
+            }
+        } else {
+            // Always update the registered alarm in this case (unlike remove()).
+            updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
+        }
+    }
+    return oldAlarms;
+}
+
 void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
     if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
     mRegisteredAlarmTimeSec = timestampSec;
diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/AnomalyMonitor.h
index d78be54..7c6e5e8 100644
--- a/cmds/statsd/src/AnomalyMonitor.h
+++ b/cmds/statsd/src/AnomalyMonitor.h
@@ -21,12 +21,14 @@
 #include <indexed_priority_queue.h>
 #include <utils/RefBase.h>
 
+#include <unordered_set>
 #include <queue>
 #include <vector>
 
 using namespace android;
 
 using android::os::IStatsCompanionService;
+using std::unordered_set;
 
 namespace android {
 namespace os {
@@ -86,6 +88,13 @@
     void remove(sp<const AnomalyAlarm> alarm);
 
     /**
+     * Returns and removes all alarms whose timestamp <= the given timestampSec.
+     * Always updates the registered alarm if return is non-empty.
+     */
+    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>
+                    popSoonerThan(uint32_t timestampSec);
+
+    /**
      * Returns the projected alarm timestamp that is registered with
      * StatsCompanionService. This may not be equal to the soonest alarm,
      * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it.
diff --git a/cmds/statsd/src/PackageInfoListener.h b/cmds/statsd/src/PackageInfoListener.h
new file mode 100644
index 0000000..476c1d9
--- /dev/null
+++ b/cmds/statsd/src/PackageInfoListener.h
@@ -0,0 +1,38 @@
+/*
+ * 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 STATSD_PACKAGE_INFO_LISTENER_H
+#define STATSD_PACKAGE_INFO_LISTENER_H
+
+#include <utils/RefBase.h>
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PackageInfoListener : public virtual android::RefBase {
+public:
+    // Uid map will notify this listener that the app with apk name and uid has been upgraded to
+    // the specified version.
+    virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif //STATSD_PACKAGE_INFO_LISTENER_H
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 117fb5e..f877ef3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,7 +20,6 @@
 #include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
 #include <log/log_event_list.h>
 #include <metrics/CountMetricProducer.h>
-#include <parse_util.h>
 #include <utils/Errors.h>
 
 using namespace android;
@@ -32,7 +31,9 @@
 namespace os {
 namespace statsd {
 
-StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs") {
+StatsLogProcessor::StatsLogProcessor(const sp<UidMap> &uidMap)
+        : m_dropbox_writer("all-logs"), m_UidMap(uidMap)
+{
     // hardcoded config
     // this should be called from StatsService when it receives a statsd_config
     UpdateConfig(0, buildFakeConfig());
@@ -41,28 +42,6 @@
 StatsLogProcessor::~StatsLogProcessor() {
 }
 
-StatsdConfig StatsLogProcessor::buildFakeConfig() {
-    // HACK: Hard code a test metric for counting screen on events...
-    StatsdConfig config;
-    config.set_config_id(12345L);
-
-    CountMetric* metric = config.add_count_metric();
-    metric->set_metric_id(20150717L);
-    metric->set_what("SCREEN_IS_ON");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
-
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()
-            ->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)
-            ->set_eq_int(2/*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-    return config;
-}
-
 // TODO: what if statsd service restarts? How do we know what logs are already processed before?
 void StatsLogProcessor::OnLogEvent(const log_msg& msg) {
     // TODO: Use EventMetric to filter the events we want to log.
@@ -83,7 +62,14 @@
 
     ALOGD("Updated configuration for source %i", config_source);
 
-    mMetricsManagers.insert({config_source, std::make_unique<MetricsManager>(config)});
+    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
+    if (newMetricsManager->isConfigValid()) {
+        mMetricsManagers.insert({config_source, std::move(newMetricsManager)});
+        ALOGD("StatsdConfig valid");
+    } else {
+        // If there is any error in the config, don't use it.
+        ALOGD("StatsdConfig NOT valid");
+    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 88c63fa..05e441c 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -16,11 +16,12 @@
 #ifndef STATS_LOG_PROCESSOR_H
 #define STATS_LOG_PROCESSOR_H
 
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "DropboxWriter.h"
 #include "LogReader.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "metrics/MetricsManager.h"
-#include "parse_util.h"
+#include "stats_util.h"
+#include "UidMap.h"
 
 #include <log/logprint.h>
 #include <stdio.h>
@@ -32,7 +33,7 @@
 
 class StatsLogProcessor : public LogListener {
 public:
-    StatsLogProcessor();
+    StatsLogProcessor(const sp<UidMap> &uidMap);
     virtual ~StatsLogProcessor();
 
     virtual void OnLogEvent(const log_msg& msg);
@@ -45,7 +46,7 @@
 
     std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers;
 
-    static StatsdConfig buildFakeConfig();
+    sp<UidMap> m_UidMap; // Reference to the UidMap to lookup app name and version for each uid.
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 9baeebb..b496404 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -40,7 +40,8 @@
 namespace statsd {
 
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mAnomalyMonitor(new AnomalyMonitor(2)), mStatsPullerManager()  // TODO: Change this based on the config
+    :   mAnomalyMonitor(new AnomalyMonitor(2)),m_UidMap(new UidMap()), mStatsPullerManager()
+    // TODO: Change AnomalyMonitor initialization based on the config
 {
     ALOGD("stats service constructed");
 }
@@ -131,6 +132,9 @@
         if (!args[0].compare(String8("config"))) {
             return doLoadConfig(in);
         }
+        if (!args[0].compare(String8("print-uid-map"))) {
+            return doPrintUidMap(out);
+        }
     }
 
     printCmdHelp(out);
@@ -153,6 +157,43 @@
     }
 }
 
+Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+                                      const vector<String16>& app) {
+    if (DEBUG) ALOGD("StatsService::informAllUidData was called");
+
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                                         "Only system uid can call informAllUidData");
+    }
+
+    m_UidMap->updateMap(uid, version, app);
+    if (DEBUG) ALOGD("StatsService::informAllUidData succeeded");
+
+    return Status::ok();
+}
+
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) {
+    if (DEBUG) ALOGD("StatsService::informOnePackage was called");
+
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                                         "Only system uid can call informOnePackage");
+    }
+    m_UidMap->updateApp(app, uid, version);
+    return Status::ok();
+}
+
+Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+    if (DEBUG) ALOGD("StatsService::informOnePackageRemoved was called");
+
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                                         "Only system uid can call informOnePackageRemoved");
+    }
+    m_UidMap->removeApp(app, uid);
+    return Status::ok();
+}
+
 Status StatsService::informAnomalyAlarmFired() {
     if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called");
 
@@ -261,9 +302,15 @@
     return DropboxReader::readStatsLogs(out, args[1].string(), msec);
 }
 
+status_t StatsService::doPrintUidMap(FILE* out) {
+    m_UidMap->printUidMap(out);
+    return NO_ERROR;
+}
+
 void StatsService::printCmdHelp(FILE* out) {
     fprintf(out, "Usage:\n");
     fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
+    fprintf(out, "\t print-uid-map Prints the UID, app name, version mapping.\n");
     fprintf(out,
             "\t config\t Loads a new config from command-line (must be proto in wire-encoded "
             "format).\n");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 9642279..541f7e8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -20,6 +20,8 @@
 #include "AnomalyMonitor.h"
 #include "StatsLogProcessor.h"
 #include "StatsPullerManager.h"
+#include "StatsPuller.h"
+#include "UidMap.h"
 
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
@@ -60,6 +62,11 @@
 
     virtual Status informPollAlarmFired();
 
+    virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+                                    const vector<String16>& app);
+    virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version);
+    virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+
     virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor);
 
     // TODO: public for testing since statsd doesn't run when system starts. Change to private
@@ -71,10 +78,16 @@
     // TODO: Should be private. Temporarily public for testing purposes only.
     const sp<AnomalyMonitor> mAnomalyMonitor;
 
+    sp<UidMap> getUidMap() {
+        return m_UidMap;
+    }
+
     /** Fetches and returns the StatsCompanionService. */
     static sp<IStatsCompanionService> getStatsCompanionService();
 
 private:
+    sp<UidMap> m_UidMap; // Reference to the UID map needed for translating UID to app name/version.
+
     sp<StatsLogProcessor> m_processor;  // Reference to the processor for updating configs.
 
     status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
@@ -84,6 +97,8 @@
     status_t doLoadConfig(FILE* in);
 
     StatsPullerManager mStatsPullerManager;
+
+    status_t doPrintUidMap(FILE* out);
 };
 
 // --- StatsdDeathRecipient ---
diff --git a/cmds/statsd/src/UidMap.cpp b/cmds/statsd/src/UidMap.cpp
new file mode 100644
index 0000000..76a7f3f
--- /dev/null
+++ b/cmds/statsd/src/UidMap.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, versionCode 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "UidMap.h"
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool UidMap::hasApp(int uid, const string& packageName) const {
+    lock_guard<mutex> lock(mMutex);
+
+    auto range = mMap.equal_range(uid);
+    for (auto it = range.first; it != range.second; ++it) {
+        if (it->second.packageName == packageName) {
+            return true;
+        }
+    }
+    return false;
+}
+
+int UidMap::getAppVersion(int uid, const string& packageName) const {
+    lock_guard<mutex> lock(mMutex);
+
+    auto range = mMap.equal_range(uid);
+    for (auto it = range.first; it != range.second; ++it) {
+        if (it->second.packageName == packageName) {
+            return it->second.versionCode;
+        }
+    }
+    return 0;
+}
+
+void UidMap::updateMap(const vector <int32_t> &uid, const vector <int32_t> &versionCode,
+                       const vector <String16> &packageName) {
+    lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
+
+    mMap.clear();
+    for (unsigned long j=0; j<uid.size(); j++) {
+        mMap.insert(make_pair(uid[j], AppData(string(String8(packageName[j]).string()),
+                                              versionCode[j])));
+    }
+
+    if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto
+        for (unsigned long j=0; j<uid.size(); j++) {
+            auto t = mOutput.add_initial();
+            t->set_app(string(String8(packageName[j]).string()));
+            t->set_version(int(versionCode[j]));
+            t->set_uid(uid[j]);
+        }
+    }
+}
+
+void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode){
+    lock_guard<mutex> lock(mMutex);
+
+    string app = string(String8(app_16).string());
+
+    // Notify any interested producers that this app has updated
+    for (auto it : mSubscribers) {
+        it->notifyAppUpgrade(app, uid, versionCode);
+    }
+
+    auto log = mOutput.add_changes();
+    log->set_deletion(false);
+    //log.timestamp = TODO: choose how timestamps are computed
+    log->set_app(app);
+    log->set_uid(uid);
+    log->set_version(versionCode);
+
+    auto range = mMap.equal_range(int(uid));
+    for (auto it = range.first; it != range.second; ++it) {
+        if (it->second.packageName == app) {
+            it->second.versionCode = int(versionCode);
+            return;
+        }
+        ALOGD("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
+        return;
+    }
+
+    // Otherwise, we need to add an app at this uid.
+    mMap.insert(make_pair(uid, AppData(app, int(versionCode))));
+}
+
+
+void UidMap::removeApp(const String16& app_16, const int32_t& uid){
+    lock_guard<mutex> lock(mMutex);
+
+    string app = string(String8(app_16).string());
+
+    auto log = mOutput.add_changes();
+    log->set_deletion(true);
+    //log.timestamp = TODO: choose how timestamps are computed
+    log->set_app(app);
+    log->set_uid(uid);
+
+    auto range = mMap.equal_range(int(uid));
+    for (auto it = range.first; it != range.second; ++it) {
+        if (it->second.packageName == app) {
+            mMap.erase(it);
+            return;
+        }
+    }
+    ALOGD("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid);
+    return;
+}
+
+void UidMap::addListener(sp<PackageInfoListener> producer) {
+    lock_guard<mutex> lock(mMutex); // Lock for updates
+    mSubscribers.insert(producer);
+}
+
+void UidMap::removeListener(sp<PackageInfoListener> producer) {
+    lock_guard<mutex> lock(mMutex); // Lock for updates
+    mSubscribers.erase(producer);
+}
+
+UidMapping UidMap::getAndClearOutput() {
+    lock_guard<mutex> lock(mMutex); // Lock for updates
+
+    auto ret = UidMapping(mOutput); // Copy that will be returned.
+    mOutput.Clear();
+
+    // Re-initialize the initial state for the outputs. This results in extra data being uploaded
+    // but helps ensure we can't re-construct the UID->app name, versionCode mapping in server.
+    for (auto it : mMap) {
+        auto t = mOutput.add_initial();
+        t->set_app(it.second.packageName);
+        t->set_version(it.second.versionCode);
+        t->set_uid(it.first);
+    }
+
+    return ret;
+}
+
+void UidMap::printUidMap(FILE* out) {
+    lock_guard<mutex> lock(mMutex);
+
+    for (auto it : mMap) {
+        fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, it.first);
+    }
+}
+
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/UidMap.h b/cmds/statsd/src/UidMap.h
new file mode 100644
index 0000000..1481010
--- /dev/null
+++ b/cmds/statsd/src/UidMap.h
@@ -0,0 +1,97 @@
+/*
+ * 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 STATSD_UIDMAP_H
+#define STATSD_UIDMAP_H
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "PackageInfoListener.h"
+
+#include <binder/IResultReceiver.h>
+#include <binder/IShellCallback.h>
+#include <log/logprint.h>
+#include <mutex>
+#include <string>
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <utils/RefBase.h>
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+struct AppData {
+    const string packageName;
+    int versionCode;
+
+    AppData(const string& a, const int v) : packageName(a), versionCode(v) {};
+};
+
+// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
+// at any given moment. This map must be updated by StatsCompanionService.
+class UidMap : public virtual android::RefBase  {
+public:
+    /*
+     * All three inputs must be the same size, and the jth element in each array refers to the same
+     * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
+     */
+    void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+                   const vector<String16>& packageName);
+
+    // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
+    bool hasApp(int uid, const string& packageName) const;
+
+    int getAppVersion(int uid, const string& packageName) const;
+
+    void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+    void removeApp(const String16& packageName, const int32_t& uid);
+
+    // Helper for debugging contents of this uid map. Can be triggered with:
+    // adb shell cmd stats print-uid-map
+    void printUidMap(FILE* out);
+
+    // Commands for indicating to the map that a producer should be notified if an app is updated.
+    // This allows the metric producer to distinguish when the same uid or app represents a
+    // different version of an app.
+    void addListener(sp<PackageInfoListener> producer);
+    // Remove the listener from the set of metric producers that subscribe to updates.
+    void removeListener(sp<PackageInfoListener> producer);
+
+    // Grabs the current output contents and then clears it.
+    UidMapping getAndClearOutput();
+
+private:
+    // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
+    mutable mutex mMutex;
+
+    std::unordered_multimap<int, AppData> mMap;
+
+    // We prepare the output proto as apps are updated, so that we can grab the current output.
+    UidMapping mOutput;
+
+    // Metric producers that should be notified if there's an upgrade in any app.
+    set<sp<PackageInfoListener>> mSubscribers;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif //STATSD_UIDMAP_H
+
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
new file mode 100644
index 0000000..6188383
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -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.
+ */
+#define LOG_TAG "CombinationConditionTracker"
+#define DEBUG true  // STOPSHIP if true
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CombinationConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index)
+    : ConditionTracker(name, index) {
+    VLOG("creating CombinationConditionTracker %s", mName.c_str());
+}
+
+CombinationConditionTracker::~CombinationConditionTracker() {
+    VLOG("~CombinationConditionTracker() %s", mName.c_str());
+}
+
+bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig,
+                                       const vector<sp<ConditionTracker>>& allConditionTrackers,
+                                       const unordered_map<string, int>& conditionNameIndexMap,
+                                       vector<bool>& stack) {
+    VLOG("Combiniation condition init() %s", mName.c_str());
+    if (mInitialized) {
+        return true;
+    }
+
+    // mark this node as visited in the recursion stack.
+    stack[mIndex] = true;
+
+    Condition_Combination combinationCondition = allConditionConfig[mIndex].combination();
+
+    if (!combinationCondition.has_operation()) {
+        return false;
+    }
+    mLogicalOperation = combinationCondition.operation();
+
+    if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) {
+        return false;
+    }
+
+    for (string child : combinationCondition.condition()) {
+        auto it = conditionNameIndexMap.find(child);
+
+        if (it == conditionNameIndexMap.end()) {
+            ALOGW("Condition %s not found in the config", child.c_str());
+            return false;
+        }
+
+        int childIndex = it->second;
+        const auto& childTracker = allConditionTrackers[childIndex];
+        // if the child is a visited node in the recursion -> circle detected.
+        if (stack[childIndex]) {
+            ALOGW("Circle detected!!!");
+            return false;
+        }
+
+        bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
+                                                     conditionNameIndexMap, stack);
+
+        if (!initChildSucceeded) {
+            ALOGW("Child initialization failed %s ", child.c_str());
+            return false;
+        } else {
+            ALOGW("Child initialization success %s ", child.c_str());
+        }
+
+        mChildren.push_back(childIndex);
+
+        mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
+                             childTracker->getLogTrackerIndex().end());
+    }
+
+    // unmark this node in the recursion stack.
+    stack[mIndex] = false;
+
+    mInitialized = true;
+
+    return true;
+}
+
+bool CombinationConditionTracker::evaluateCondition(
+        const LogEventWrapper& event, const std::vector<MatchingState>& eventMatcherValues,
+        const std::vector<sp<ConditionTracker>>& mAllConditions,
+        std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) {
+    // value is up to date.
+    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+        return false;
+    }
+
+    for (const int childIndex : mChildren) {
+        if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+            const sp<ConditionTracker>& child = mAllConditions[childIndex];
+            child->evaluateCondition(event, eventMatcherValues, mAllConditions, conditionCache,
+                                     changedCache);
+        }
+    }
+
+    ConditionState newCondition =
+            evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+
+    bool changed = (mConditionState != newCondition);
+    mConditionState = newCondition;
+
+    conditionCache[mIndex] = mConditionState;
+
+    changedCache[mIndex] = changed;
+    return changed;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
new file mode 100644
index 0000000..38780e7
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -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.
+ */
+
+#ifndef COMBINATION_CONDITION_TRACKER_H
+#define COMBINATION_CONDITION_TRACKER_H
+
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class CombinationConditionTracker : public virtual ConditionTracker {
+public:
+    CombinationConditionTracker(const std::string& name, const int index);
+
+    ~CombinationConditionTracker();
+
+    bool init(const std::vector<Condition>& allConditionConfig,
+              const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+              const std::unordered_map<std::string, int>& conditionNameIndexMap,
+              std::vector<bool>& stack) override;
+
+    bool evaluateCondition(const LogEventWrapper& event,
+                           const std::vector<MatchingState>& eventMatcherValues,
+                           const std::vector<sp<ConditionTracker>>& mAllConditions,
+                           std::vector<ConditionState>& conditionCache,
+                           std::vector<bool>& changedCache) override;
+
+private:
+    LogicalOperation mLogicalOperation;
+    // Store index of the children Conditions.
+    // We don't store string name of the Children, because we want to get rid of the hash map to
+    // map the name to object. We don't want to store smart pointers to children, because it
+    // increases the risk of circular dependency and memory leak.
+    std::vector<int> mChildren;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // COMBINATION_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
new file mode 100644
index 0000000..2da8fa0
--- /dev/null
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -0,0 +1,105 @@
+/*
+ * 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 CONDITION_TRACKER_H
+#define CONDITION_TRACKER_H
+
+#include <cutils/log.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include "../matchers/LogMatchingTracker.h"
+#include "../matchers/matcher_util.h"
+#include "condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ConditionTracker : public virtual RefBase {
+public:
+    ConditionTracker(const std::string& name, const int index)
+        : mName(name),
+          mIndex(index),
+          mInitialized(false),
+          mConditionState(ConditionState::kUnknown),
+          mTrackerIndex(){};
+
+    virtual ~ConditionTracker(){};
+
+    // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
+    // be done in the constructor, but we do it separately because (1) easy to return a bool to
+    // indicate whether the initialization is successful. (2) makes unit test easier.
+    // allConditionConfig: the list of all Condition config from statsd_config.
+    // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+    //                       need to call init() on children conditions)
+    // conditionNameIndexMap: the mapping from condition name to its index.
+    // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
+    virtual bool init(const std::vector<Condition>& allConditionConfig,
+                      const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                      const std::unordered_map<std::string, int>& conditionNameIndexMap,
+                      std::vector<bool>& stack) = 0;
+
+    // evaluate current condition given the new event.
+    // return true if the condition state changed, false if the condition state is not changed.
+    // event: the new log event
+    // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
+    //                     event before ConditionTrackers, because ConditionTracker depends on
+    //                     LogMatchingTrackers.
+    // mAllConditions: the list of all ConditionTracker
+    // conditionCache: the cached results of the ConditionTrackers for this new event.
+    // changedCache: the bit map to record whether the condition has changed.
+    virtual bool evaluateCondition(const LogEventWrapper& event,
+                                   const std::vector<MatchingState>& eventMatcherValues,
+                                   const std::vector<sp<ConditionTracker>>& mAllConditions,
+                                   std::vector<ConditionState>& conditionCache,
+                                   std::vector<bool>& changedCache) = 0;
+
+    // Return the current condition state.
+    virtual ConditionState isConditionMet() {
+        ALOGW("Condition %s value %d", mName.c_str(), mConditionState);
+        return mConditionState;
+    };
+
+    // return the list of LogMatchingTracker index that this ConditionTracker uses.
+    virtual const std::set<int>& getLogTrackerIndex() const {
+        return mTrackerIndex;
+    }
+
+protected:
+    // We don't really need the string name, but having a name here makes log messages
+    // easy to debug.
+    const std::string mName;
+
+    // the index of this condition in the manager's condition list.
+    const int mIndex;
+
+    // if it's properly initialized.
+    bool mInitialized;
+
+    // current condition state.
+    ConditionState mConditionState;
+
+    // the list of LogMatchingTracker index that this ConditionTracker uses.
+    std::set<int> mTrackerIndex;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
new file mode 100644
index 0000000..e78c0de
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "Stats_SimpleConditionTracker"
+#define DEBUG true  // STOPSHIP if true
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleConditionTracker::SimpleConditionTracker(
+        const string& name, const int index, const SimpleCondition& simpleCondition,
+        const unordered_map<string, int>& trackerNameIndexMap)
+    : ConditionTracker(name, index) {
+    VLOG("creating SimpleConditionTracker %s", mName.c_str());
+    mCountNesting = simpleCondition.count_nesting();
+
+    if (simpleCondition.has_start()) {
+        auto pair = trackerNameIndexMap.find(simpleCondition.start());
+        if (pair == trackerNameIndexMap.end()) {
+            ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str());
+            return;
+        }
+        mStartLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStartLogMatcherIndex);
+    } else {
+        mStartLogMatcherIndex = -1;
+    }
+
+    if (simpleCondition.has_stop()) {
+        auto pair = trackerNameIndexMap.find(simpleCondition.stop());
+        if (pair == trackerNameIndexMap.end()) {
+            ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+            return;
+        }
+        mStopLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopLogMatcherIndex);
+    } else {
+        mStopLogMatcherIndex = -1;
+    }
+
+    if (simpleCondition.has_stop_all()) {
+        auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
+        if (pair == trackerNameIndexMap.end()) {
+            ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+            return;
+        }
+        mStopAllLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopAllLogMatcherIndex);
+    } else {
+        mStopAllLogMatcherIndex = -1;
+    }
+
+    mInitialized = true;
+}
+
+SimpleConditionTracker::~SimpleConditionTracker() {
+    VLOG("~SimpleConditionTracker()");
+}
+
+bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
+                                  const vector<sp<ConditionTracker>>& allConditionTrackers,
+                                  const unordered_map<string, int>& conditionNameIndexMap,
+                                  vector<bool>& stack) {
+    // SimpleConditionTracker does not have dependency on other conditions, thus we just return
+    // if the initialization was successful.
+    return mInitialized;
+}
+
+bool SimpleConditionTracker::evaluateCondition(const LogEventWrapper& event,
+                                               const vector<MatchingState>& eventMatcherValues,
+                                               const vector<sp<ConditionTracker>>& mAllConditions,
+                                               vector<ConditionState>& conditionCache,
+                                               vector<bool>& changedCache) {
+    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+        // it has been evaluated.
+        VLOG("Yes, already evaluated, %s %d", mName.c_str(), mConditionState);
+        return false;
+    }
+
+    // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+    ConditionState newCondition = mConditionState;
+    // Note: The order to evaluate the following start, stop, stop_all matters.
+    // The priority of overwrite is stop_all > stop > start.
+    if (mStartLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
+        newCondition = ConditionState::kTrue;
+    }
+
+    if (mStopLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
+        newCondition = ConditionState::kFalse;
+    }
+
+    if (mStopAllLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+        newCondition = ConditionState::kFalse;
+    }
+
+    bool changed = (mConditionState != newCondition);
+    mConditionState = newCondition;
+    conditionCache[mIndex] = mConditionState;
+    changedCache[mIndex] = changed;
+    return changed;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
new file mode 100644
index 0000000..41e1707
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -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.
+ */
+
+#ifndef SIMPLE_CONDITION_TRACKER_H
+#define SIMPLE_CONDITION_TRACKER_H
+
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleConditionTracker : public virtual ConditionTracker {
+public:
+    SimpleConditionTracker(const std::string& name, const int index,
+                           const SimpleCondition& simpleCondition,
+                           const std::unordered_map<std::string, int>& trackerNameIndexMap);
+
+    ~SimpleConditionTracker();
+
+    bool init(const std::vector<Condition>& allConditionConfig,
+              const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+              const std::unordered_map<std::string, int>& conditionNameIndexMap,
+              std::vector<bool>& stack) override;
+
+    bool evaluateCondition(const LogEventWrapper& event,
+                           const std::vector<MatchingState>& eventMatcherValues,
+                           const std::vector<sp<ConditionTracker>>& mAllConditions,
+                           std::vector<ConditionState>& conditionCache,
+                           std::vector<bool>& changedCache) override;
+
+private:
+    // The index of the LogEventMatcher which defines the start.
+    int mStartLogMatcherIndex;
+
+    // The index of the LogEventMatcher which defines the end.
+    int mStopLogMatcherIndex;
+
+    // if the start end needs to be nested.
+    bool mCountNesting;
+
+    // The index of the LogEventMatcher which defines the stop all.
+    int mStopAllLogMatcherIndex;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // SIMPLE_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
new file mode 100644
index 0000000..cb07d15
--- /dev/null
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -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.
+ */
+
+#include "condition_util.h"
+
+#include <cutils/log.h>
+#include <log/event_tag_map.h>
+#include <log/log_event_list.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+#include <unordered_map>
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+ConditionState evaluateCombinationCondition(const std::vector<int>& children,
+                                            const LogicalOperation& operation,
+                                            const std::vector<ConditionState>& conditionCache) {
+    ConditionState newCondition;
+
+    bool hasUnknown = false;
+    bool hasFalse = false;
+    bool hasTrue = false;
+
+    for (auto childIndex : children) {
+        ConditionState childState = conditionCache[childIndex];
+        if (childState == ConditionState::kUnknown) {
+            hasUnknown = true;
+            break;
+        }
+        if (childState == ConditionState::kFalse) {
+            hasFalse = true;
+        }
+        if (childState == ConditionState::kTrue) {
+            hasTrue = true;
+        }
+    }
+
+    // If any child condition is in unknown state, the condition is unknown too.
+    if (hasUnknown) {
+        return ConditionState::kUnknown;
+    }
+
+    switch (operation) {
+        case LogicalOperation::AND: {
+            newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue;
+            break;
+        }
+        case LogicalOperation::OR: {
+            newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse;
+            break;
+        }
+        case LogicalOperation::NOT:
+            newCondition = (conditionCache[children[0]] == ConditionState::kFalse)
+                                   ? ConditionState::kTrue
+                                   : ConditionState::kFalse;
+            break;
+        case LogicalOperation::NAND:
+            newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse;
+            break;
+        case LogicalOperation::NOR:
+            newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
+            break;
+    }
+    return newCondition;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/ConditionTracker.h b/cmds/statsd/src/condition/condition_util.h
similarity index 60%
rename from cmds/statsd/src/metrics/ConditionTracker.h
rename to cmds/statsd/src/condition/condition_util.h
index b94d5ab..a4fcea3 100644
--- a/cmds/statsd/src/metrics/ConditionTracker.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -14,38 +14,28 @@
  * limitations under the License.
  */
 
-#ifndef CONDITION_TRACKER_H
-#define CONDITION_TRACKER_H
+#ifndef CONDITION_UTIL_H
+#define CONDITION_UTIL_H
 
-#include <utils/RefBase.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-class ConditionTracker : public RefBase {
-public:
-    ConditionTracker();
-
-    ConditionTracker(const Condition& condition);
-
-    ~ConditionTracker();
-
-    void evaluateCondition(const LogEventWrapper& event);
-
-    bool isConditionMet() const;
-
-private:
-    // this is the definition of the Condition.
-    Condition mCondition;
-
-    bool mIsConditionMet;
+enum ConditionState {
+    kNotEvaluated = -2,
+    kUnknown = -1,
+    kFalse = 0,
+    kTrue = 1,
 };
 
+ConditionState evaluateCombinationCondition(const std::vector<int>& children,
+                                            const LogicalOperation& operation,
+                                            const std::vector<ConditionState>& conditionCache);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
-#endif  // CONDITION_TRACKER_H
+#endif  // CONDITION_UTIL_H
diff --git a/cmds/statsd/src/indexed_priority_queue.h b/cmds/statsd/src/indexed_priority_queue.h
index c749c3e..81e8b3d 100644
--- a/cmds/statsd/src/indexed_priority_queue.h
+++ b/cmds/statsd/src/indexed_priority_queue.h
@@ -55,6 +55,8 @@
     void push(sp<const AA> a);
     /** Removes a from the priority queue. If not present or a==nullptr, does nothing. */
     void remove(sp<const AA> a);
+    /** Removes the top element, if there is one. */
+    void pop();
     /** Removes all elements. */
     void clear();
     /** Returns whether priority queue contains a (not just a copy of a, but a itself). */
@@ -127,6 +129,28 @@
     sift_down(idx);
 }
 
+// The same as, but slightly more efficient than, remove(top()).
+template <class AA, class Comparator>
+void indexed_priority_queue<AA, Comparator>::pop() {
+  sp<const AA> a = top();
+  if (a == nullptr) return;
+  const size_t idx = 1;
+  if (idx == size()) {  // if a is the last element
+    pq.pop_back();
+    indices.erase(a);
+    return;
+  }
+  // move last element (guaranteed not to be at idx) to idx, then delete a
+  sp<const AA> last_a = pq.back();
+  pq[idx] = last_a;
+  pq.pop_back();
+  indices[last_a] = idx;
+  indices.erase(a);
+
+  // get the heap back in order (since the element at idx is not in order)
+  sift_down(idx);
+}
+
 template <class AA, class Comparator>
 void indexed_priority_queue<AA, Comparator>::clear() {
     pq.clear();
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index b303321..37477dc 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,6 +20,7 @@
 #include "LogReader.h"
 #include "StatsLogProcessor.h"
 #include "StatsService.h"
+#include "UidMap.h"
 
 #include <binder/IInterface.h>
 #include <binder/IPCThreadState.h>
@@ -56,7 +57,7 @@
 
     // Put the printer one first, so it will print before the real ones.
     reader->AddListener(new LogEntryPrinter(STDOUT_FILENO));
-    sp<StatsLogProcessor> main_processor = new StatsLogProcessor();
+    sp<StatsLogProcessor> main_processor = new StatsLogProcessor(data->service->getUidMap());
     data->service->setProcessor(main_processor);
     reader->AddListener(main_processor);
 
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
new file mode 100644
index 0000000..9f9b648
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#include "CombinationLogMatchingTracker.h"
+
+#include <cutils/log.h>
+#include "matcher_util.h"
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index)
+    : LogMatchingTracker(name, index) {
+}
+
+CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
+}
+
+bool CombinationLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+                                         const vector<sp<LogMatchingTracker>>& allTrackers,
+                                         const unordered_map<string, int>& matcherMap,
+                                         vector<bool>& stack) {
+    if (mInitialized) {
+        return true;
+    }
+
+    // mark this node as visited in the recursion stack.
+    stack[mIndex] = true;
+
+    LogEntryMatcher_Combination matcher = allLogMatchers[mIndex].combination();
+
+    // LogicalOperation is missing in the config
+    if (!matcher.has_operation()) {
+        return false;
+    }
+
+    mLogicalOperation = matcher.operation();
+
+    if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
+        return false;
+    }
+
+    for (const string& child : matcher.matcher()) {
+        auto pair = matcherMap.find(child);
+        if (pair == matcherMap.end()) {
+            ALOGW("Matcher %s not found in the config", child.c_str());
+            return false;
+        }
+
+        int childIndex = pair->second;
+
+        // if the child is a visited node in the recursion -> circle detected.
+        if (stack[childIndex]) {
+            ALOGE("Circle detected in matcher config");
+            return false;
+        }
+
+        if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
+            ALOGW("child matcher init failed %s", child.c_str());
+            return false;
+        }
+
+        mChildren.push_back(childIndex);
+
+        const set<int>& childTagIds = allTrackers[childIndex]->getTagIds();
+        mTagIds.insert(childTagIds.begin(), childTagIds.end());
+    }
+
+    mInitialized = true;
+    // unmark this node in the recursion stack.
+    stack[mIndex] = false;
+    return true;
+}
+
+void CombinationLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+                                               const vector<sp<LogMatchingTracker>>& allTrackers,
+                                               vector<MatchingState>& matcherResults) {
+    // this event has been processed.
+    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+        return;
+    }
+
+    if (mTagIds.find(event.tagId) == mTagIds.end()) {
+        matcherResults[mIndex] = MatchingState::kNotMatched;
+        return;
+    }
+
+    // evaluate children matchers if they haven't been evaluated.
+    for (const int childIndex : mChildren) {
+        if (matcherResults[childIndex] == MatchingState::kNotComputed) {
+            const sp<LogMatchingTracker>& child = allTrackers[childIndex];
+            child->onLogEvent(event, allTrackers, matcherResults);
+        }
+    }
+
+    bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
+    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
new file mode 100644
index 0000000..51ee232
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -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.
+ */
+#ifndef COMBINATION_LOG_MATCHING_TRACKER_H
+#define COMBINATION_LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Represents a LogEntryMatcher_Combination in the StatsdConfig.
+class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+    CombinationLogMatchingTracker(const std::string& name, const int index);
+
+    bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+              const std::vector<sp<LogMatchingTracker>>& allTrackers,
+              const std::unordered_map<std::string, int>& matcherMap,
+              std::vector<bool>& stack);
+
+    ~CombinationLogMatchingTracker();
+
+    void onLogEvent(const LogEventWrapper& event,
+                    const std::vector<sp<LogMatchingTracker>>& allTrackers,
+                    std::vector<MatchingState>& matcherResults) override;
+
+private:
+    LogicalOperation mLogicalOperation;
+
+    std::vector<int> mChildren;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // COMBINATION_LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
new file mode 100644
index 0000000..4244bd5
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -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.
+ */
+
+#ifndef LOG_MATCHING_TRACKER_H
+#define LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <set>
+#include <unordered_map>
+
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matcher_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class LogMatchingTracker : public virtual RefBase {
+public:
+    LogMatchingTracker(const std::string& name, const int index)
+        : mName(name), mIndex(index), mInitialized(false){};
+
+    virtual ~LogMatchingTracker(){};
+
+    // Initialize this LogMatchingTracker.
+    // allLogMatchers: the list of the LogEntryMatcher proto config. This is needed because we don't
+    //                 store the proto object in memory. We only need it during initilization.
+    // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
+    //              allLogMatchers. This is needed because the initialization is done recursively
+    //              for CombinationLogMatchingTrackers using DFS.
+    // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
+    //        circle dependency.
+    virtual bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+                      const std::vector<sp<LogMatchingTracker>>& allTrackers,
+                      const std::unordered_map<std::string, int>& matcherMap,
+                      std::vector<bool>& stack) = 0;
+
+    // Called when a log event comes.
+    // event: the log event.
+    // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing
+    //              is done recursively.
+    // matcherResults: The cached results for all matchers for this event. Parent matchers can
+    //                 directly access the children's matching results if they have been evaluated.
+    //                 Otherwise, call children matchers' onLogEvent.
+    virtual void onLogEvent(const LogEventWrapper& event,
+                            const std::vector<sp<LogMatchingTracker>>& allTrackers,
+                            std::vector<MatchingState>& matcherResults) = 0;
+
+    // Get the tagIds that this matcher cares about. The combined collection is stored
+    // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
+    // some memory but hopefully it can save us much CPU time when there is flood of events.
+    virtual const std::set<int>& getTagIds() const {
+        return mTagIds;
+    }
+
+protected:
+    // Name of this matching. We don't really need the name, but it makes log message easy to debug.
+    const std::string mName;
+
+    // Index of this LogMatchingTracker in MetricsManager's container.
+    const int mIndex;
+
+    // Whether this LogMatchingTracker has been properly initialized.
+    bool mInitialized;
+
+    // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
+    // return kNotMatched when we receive an event with an id not in the list. This is especially
+    // useful when we have a complex CombinationLogMatcherTracker.
+    // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
+    // LogMatchingTracker cares is only a few.
+    std::set<int> mTagIds;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
new file mode 100644
index 0000000..1c83039
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SimpleLogMatchingTracker"
+#define DEBUG true  // STOPSHIP if true
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleLogMatchingTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
+                                                   const SimpleLogEntryMatcher& matcher)
+    : LogMatchingTracker(name, index), mMatcher(matcher) {
+    for (int i = 0; i < matcher.tag_size(); i++) {
+        mTagIds.insert(matcher.tag(i));
+    }
+    mInitialized = true;
+}
+
+SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
+}
+
+bool SimpleLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+                                    const vector<sp<LogMatchingTracker>>& allTrackers,
+                                    const unordered_map<string, int>& matcherMap,
+                                    vector<bool>& stack) {
+    // no need to do anything.
+    return true;
+}
+
+void SimpleLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+                                          const vector<sp<LogMatchingTracker>>& allTrackers,
+                                          vector<MatchingState>& matcherResults) {
+    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+        VLOG("Matcher %s already evaluated ", mName.c_str());
+        return;
+    }
+
+    if (mTagIds.find(event.tagId) == mTagIds.end()) {
+        matcherResults[mIndex] = MatchingState::kNotMatched;
+        return;
+    }
+
+    bool matched = matchesSimple(mMatcher, event);
+    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+    VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
new file mode 100644
index 0000000..65dbe64
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -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.
+ */
+
+#ifndef SIMPLE_LOG_MATCHING_TRACKER_H
+#define SIMPLE_LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+    SimpleLogMatchingTracker(const std::string& name, const int index,
+                             const SimpleLogEntryMatcher& matcher);
+
+    ~SimpleLogMatchingTracker();
+
+    bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+              const std::vector<sp<LogMatchingTracker>>& allTrackers,
+              const std::unordered_map<std::string, int>& matcherMap,
+              std::vector<bool>& stack) override;
+
+    void onLogEvent(const LogEventWrapper& event,
+                    const std::vector<sp<LogMatchingTracker>>& allTrackers,
+                    std::vector<MatchingState>& matcherResults) override;
+
+private:
+    const SimpleLogEntryMatcher mMatcher;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // SIMPLE_LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
similarity index 69%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
rename to cmds/statsd/src/matchers/matcher_util.cpp
index ab7b2b1d..557c032 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -14,26 +14,28 @@
  * limitations under the License.
  */
 
-#include "LogEntryMatcherManager.h"
+#include "matcher_util.h"
 #include <cutils/log.h>
 #include <log/event_tag_map.h>
 #include <log/log_event_list.h>
 #include <log/logprint.h>
 #include <utils/Errors.h>
 #include <unordered_map>
+#include "LogMatchingTracker.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "parse_util.h"
+#include "stats_util.h"
 
 using std::set;
 using std::string;
 using std::unordered_map;
+using std::vector;
 
 namespace android {
 namespace os {
 namespace statsd {
 
-LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) {
+LogEventWrapper parseLogEvent(log_msg msg) {
     LogEventWrapper wrapper;
     wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
     wrapper.tagId = getTagId(msg);
@@ -67,7 +69,9 @@
                     break;
                 case EVENT_TYPE_STRING:
                     if (index % 2 == 1) {
-                        wrapper.strMap[key] = elem.data.string;
+                        // without explicit calling string() constructor, there will be an
+                        // additional 0 in the end of the string.
+                        wrapper.strMap[key] = string(elem.data.string);
                     }
                     index++;
                     break;
@@ -99,57 +103,56 @@
     return wrapper;
 }
 
-bool LogEntryMatcherManager::matches(const LogEntryMatcher& matcher, const LogEventWrapper& event) {
-    const int tagId = event.tagId;
-    const unordered_map<int, long>& intMap = event.intMap;
-    const unordered_map<int, string>& strMap = event.strMap;
-    const unordered_map<int, float>& floatMap = event.floatMap;
-    const unordered_map<int, bool>& boolMap = event.boolMap;
-
-    if (matcher.has_combination()) {  // Need to evaluate composite matching
-        switch (matcher.combination().operation()) {
-            case LogicalOperation::AND:
-                for (auto nestedMatcher : matcher.combination().matcher()) {
-                    if (!matches(nestedMatcher, event)) {
-                        return false;  // return false if any nested matcher is false;
-                    }
+bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
+                      const vector<MatchingState>& matcherResults) {
+    bool matched;
+    switch (operation) {
+        case LogicalOperation::AND: {
+            matched = true;
+            for (const int childIndex : children) {
+                if (matcherResults[childIndex] != MatchingState::kMatched) {
+                    matched = false;
+                    break;
                 }
-                return true;  // Otherwise, return true.
-            case LogicalOperation::OR:
-                for (auto nestedMatcher : matcher.combination().matcher()) {
-                    if (matches(nestedMatcher, event)) {
-                        return true;  // return true if any nested matcher is true;
-                    }
-                }
-                return false;
-            case LogicalOperation::NOT:
-                return !matches(matcher.combination().matcher(0), event);
-
-            // Case NAND is just inverting the return statement of AND
-            case LogicalOperation::NAND:
-                for (auto nestedMatcher : matcher.combination().matcher()) {
-                    auto simple = nestedMatcher.simple_log_entry_matcher();
-                    if (!matches(nestedMatcher, event)) {
-                        return true;  // return false if any nested matcher is false;
-                    }
-                }
-                return false;  // Otherwise, return true.
-            case LogicalOperation::NOR:
-                for (auto nestedMatcher : matcher.combination().matcher()) {
-                    if (matches(nestedMatcher, event)) {
-                        return false;  // return true if any nested matcher is true;
-                    }
-                }
-                return true;
+            }
+            break;
         }
-        return false;
-    } else {
-        return matchesSimple(matcher.simple_log_entry_matcher(), event);
+        case LogicalOperation::OR: {
+            matched = false;
+            for (const int childIndex : children) {
+                if (matcherResults[childIndex] == MatchingState::kMatched) {
+                    matched = true;
+                    break;
+                }
+            }
+            break;
+        }
+        case LogicalOperation::NOT:
+            matched = matcherResults[children[0]] == MatchingState::kNotMatched;
+            break;
+        case LogicalOperation::NAND:
+            matched = false;
+            for (const int childIndex : children) {
+                if (matcherResults[childIndex] != MatchingState::kMatched) {
+                    matched = true;
+                    break;
+                }
+            }
+            break;
+        case LogicalOperation::NOR:
+            matched = true;
+            for (const int childIndex : children) {
+                if (matcherResults[childIndex] == MatchingState::kMatched) {
+                    matched = false;
+                    break;
+                }
+            }
+            break;
     }
+    return matched;
 }
 
-bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
-                                           const LogEventWrapper& event) {
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& event) {
     const int tagId = event.tagId;
     const unordered_map<int, long>& intMap = event.intMap;
     const unordered_map<int, string>& strMap = event.strMap;
@@ -249,26 +252,6 @@
     return false;
 }
 
-set<int> LogEntryMatcherManager::getTagIdsFromMatcher(const LogEntryMatcher& matcher) {
-    set<int> result;
-    switch (matcher.contents_case()) {
-        case LogEntryMatcher::kCombination:
-            for (auto sub_matcher : matcher.combination().matcher()) {
-                set<int> tagSet = getTagIdsFromMatcher(sub_matcher);
-                result.insert(tagSet.begin(), tagSet.end());
-            }
-            break;
-        case LogEntryMatcher::kSimpleLogEntryMatcher:
-            for (int i = 0; i < matcher.simple_log_entry_matcher().tag_size(); i++) {
-                result.insert(matcher.simple_log_entry_matcher().tag(i));
-            }
-            break;
-        case LogEntryMatcher::CONTENTS_NOT_SET:
-            break;
-    }
-    return result;
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/matcher_util.h
similarity index 63%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.h
rename to cmds/statsd/src/matchers/matcher_util.h
index fc8e6a1..6d8e762 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef LOG_ENTRY_MATCHER_MANAGER_H
-#define LOG_ENTRY_MATCHER_MANAGER_H
+#ifndef MATCHER_UTIL_H
+#define MATCHER_UTIL_H
 
 #include <log/log_read.h>
 #include <log/logprint.h>
@@ -25,9 +25,6 @@
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
-using std::string;
-using std::unordered_map;
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -41,26 +38,20 @@
     std::unordered_map<int, float> floatMap;
 } LogEventWrapper;
 
-/**
- * Keeps track per log entry which simple log entry matchers match.
- */
-class LogEntryMatcherManager {
-public:
-    LogEntryMatcherManager();
-
-    ~LogEntryMatcherManager(){};
-
-    static LogEventWrapper parseLogEvent(log_msg msg);
-
-    static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher);
-
-    static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper);
-
-    static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
-                              const LogEventWrapper& wrapper);
+enum MatchingState {
+    kNotComputed = -1,
+    kNotMatched = 0,
+    kMatched = 1,
 };
 
+LogEventWrapper parseLogEvent(log_msg msg);
+
+bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
+                      const std::vector<MatchingState>& matcherResults);
+
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& wrapper);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-#endif  // LOG_ENTRY_MATCHER_MANAGER_H
+#endif  // MATCHER_UTIL_H
diff --git a/cmds/statsd/src/metrics/ConditionTracker.cpp b/cmds/statsd/src/metrics/ConditionTracker.cpp
deleted file mode 100644
index 684ffdb..0000000
--- a/cmds/statsd/src/metrics/ConditionTracker.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ConditionTracker"
-#define DEBUG true  // STOPSHIP if true
-#define VLOG(...) \
-    if (DEBUG) ALOGD(__VA_ARGS__);
-
-#include "ConditionTracker.h"
-#include <cutils/log.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-ConditionTracker::ConditionTracker() : mIsConditionMet(true) {
-    VLOG("ConditionTracker()");
-}
-
-ConditionTracker::ConditionTracker(const Condition& condition)
-    : mCondition(condition), mIsConditionMet(true) {
-    VLOG("ConditionTracker()");
-}
-
-ConditionTracker::~ConditionTracker() {
-    VLOG("~ConditionTracker()");
-}
-
-void ConditionTracker::evaluateCondition(const LogEventWrapper& event) {
-    // modify condition.
-    VLOG("evaluateCondition");
-}
-
-bool ConditionTracker::isConditionMet() const {
-    VLOG("isConditionMet() %d", mIsConditionMet);
-    return mIsConditionMet;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 635777f..e98999e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,7 +21,6 @@
 
 #include "CountMetricProducer.h"
 #include "CountAnomalyTracker.h"
-#include "parse_util.h"
 
 #include <cutils/log.h>
 #include <limits.h>
@@ -33,15 +32,14 @@
 namespace os {
 namespace statsd {
 
-CountMetricProducer::CountMetricProducer(const CountMetric& metric,
-                                         const sp<ConditionTracker> condition)
+CountMetricProducer::CountMetricProducer(const CountMetric& metric, const bool hasCondition)
     : mMetric(metric),
-      mConditionTracker(condition),
-      mStartTime(std::time(nullptr)),
+      mStartTime(time(nullptr)),
       mCounter(0),
       mCurrentBucketStartTime(mStartTime),
       // TODO: read mAnomalyTracker parameters from config file.
-      mAnomalyTracker(6, 10) {
+      mAnomalyTracker(6, 10),
+      mCondition(hasCondition ? ConditionState::kUnknown : ConditionState::kTrue) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000;
@@ -52,10 +50,6 @@
     VLOG("created. bucket size %lu start_time: %lu", mBucketSize_sec, mStartTime);
 }
 
-CountMetricProducer::CountMetricProducer(const CountMetric& metric)
-    : CountMetricProducer(metric, new ConditionTracker()) {
-}
-
 CountMetricProducer::~CountMetricProducer() {
     VLOG("~CountMetricProducer() called");
 }
@@ -63,13 +57,17 @@
 void CountMetricProducer::finish() {
     // TODO: write the StatsLogReport to dropbox using
     // DropboxWriter.
-    onDumpReport();
 }
 
 void CountMetricProducer::onDumpReport() {
     VLOG("dump report now...");
 }
 
+void CountMetricProducer::onConditionChanged(const bool conditionMet) {
+    VLOG("onConditionChanged");
+    mCondition = conditionMet;
+}
+
 void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) {
     time_t eventTime = event.timestamp_ns / 1000000000;
 
@@ -78,22 +76,24 @@
         return;
     }
 
-    if (mConditionTracker->isConditionMet()) {
+    if (mCondition == ConditionState::kTrue) {
         flushCounterIfNeeded(eventTime);
         mCounter++;
         mAnomalyTracker.checkAnomaly(mCounter);
+        VLOG("metric %lld count %d", mMetric.metric_id(), mCounter);
     }
 }
 
-// When a new matched event comes in, we check if it falls into the current bucket. And flush the
-// counter to the StatsLogReport and adjust the bucket if needed.
+// When a new matched event comes in, we check if it falls into the current
+// bucket. And flush the counter to the StatsLogReport and adjust the bucket if
+// needed.
 void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) {
     if (mCurrentBucketStartTime + mBucketSize_sec > eventTime) {
         return;
     }
 
     // TODO: add a KeyValuePair to StatsLogReport.
-    ALOGD("CountMetric: dump counter %d", mCounter);
+    ALOGD("%lld:  dump counter %d", mMetric.metric_id(), mCounter);
 
     // adjust the bucket start time
     time_t numBucketsForward = (eventTime - mCurrentBucketStartTime)
@@ -106,7 +106,7 @@
     mAnomalyTracker.addPastBucket(mCounter, numBucketsForward);
     mCounter = 0;
 
-    VLOG("new bucket start time: %lu", mCurrentBucketStartTime);
+    VLOG("%lld: new bucket start time: %lu", mMetric.metric_id(), mCurrentBucketStartTime);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 7502320..370cd468 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,40 +17,41 @@
 #ifndef COUNT_METRIC_PRODUCER_H
 #define COUNT_METRIC_PRODUCER_H
 
-#include <mutex>
-#include <thread>
 #include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
+
 #include "CountAnomalyTracker.h"
-#include "ConditionTracker.h"
-#include "DropboxWriter.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/matcher_util.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
+using namespace std;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 class CountMetricProducer : public MetricProducer {
 public:
-    CountMetricProducer(const CountMetric& countMetric, const sp<ConditionTracker> condition);
-
-    CountMetricProducer(const CountMetric& countMetric);
+    CountMetricProducer(const CountMetric& countMetric, const bool hasCondition);
 
     virtual ~CountMetricProducer();
 
     void onMatchedLogEvent(const LogEventWrapper& event) override;
 
+    void onConditionChanged(const bool conditionMet) override;
+
     void finish() override;
 
     void onDumpReport() override;
 
+    // TODO: Implement this later.
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override {};
+
 private:
     const CountMetric mMetric;
 
-    const sp<ConditionTracker> mConditionTracker;
-
     const time_t mStartTime;
     // TODO: Add dimensions.
     // Counter value for the current bucket.
@@ -62,6 +63,8 @@
 
     CountAnomalyTracker mAnomalyTracker;
 
+    bool mCondition;
+
     void flushCounterIfNeeded(const time_t& newEventTime);
 };
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 44a778b..b7e9656 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -18,21 +18,27 @@
 #define METRIC_PRODUCER_H
 
 #include <log/logprint.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <utils/RefBase.h>
+#include "../matchers/matcher_util.h"
+#include "PackageInfoListener.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
 // A MetricProducer is responsible for compute one single metrics, creating stats log report, and
-// writing the report to dropbox.
-class MetricProducer {
+// writing the report to dropbox. MetricProducers should respond to package changes as required in
+// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
+// be a no-op.
+class MetricProducer : public virtual RefBase, public virtual PackageInfoListener {
 public:
     virtual ~MetricProducer(){};
 
-    // Consume the stats log if it's interesting to this metric.
+    // Consume the parsed stats log entry that already matched the "what" of the metric.
     virtual void onMatchedLogEvent(const LogEventWrapper& event) = 0;
 
+    virtual void onConditionChanged(const bool condition) = 0;
+
     // This is called when the metric collecting is done, e.g., when there is a new configuration
     // coming. MetricProducer should do the clean up, and dump existing data to dropbox.
     virtual void finish() = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index cb74206..1e65f58 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,13 +21,17 @@
 #include "MetricsManager.h"
 #include <cutils/log.h>
 #include <log/logprint.h>
+#include "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
 #include "CountMetricProducer.h"
-#include "parse_util.h"
+#include "metrics_manager_util.h"
+#include "stats_util.h"
 
 using std::make_unique;
 using std::set;
 using std::string;
-using std::unique_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -35,93 +39,90 @@
 namespace os {
 namespace statsd {
 
-MetricsManager::MetricsManager(const StatsdConfig& config) : mConfig(config), mLogMatchers() {
-    std::unordered_map<string, LogEntryMatcher> matcherMap;
-    std::unordered_map<string, sp<ConditionTracker>> conditionMap;
-
-    for (int i = 0; i < config.log_entry_matcher_size(); i++) {
-        const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
-        mMatchers.push_back(logMatcher);
-
-        matcherMap[config.log_entry_matcher(i).name()] = logMatcher;
-
-        mLogMatchers[logMatcher.name()] = vector<unique_ptr<MetricProducer>>();
-        // Collect all the tag ids that are interesting
-        set<int> tagIds = LogEntryMatcherManager::getTagIdsFromMatcher(logMatcher);
-
-        mTagIds.insert(tagIds.begin(), tagIds.end());
-    }
-
-    for (int i = 0; i < config.condition_size(); i++) {
-        const Condition& condition = config.condition(i);
-        conditionMap[condition.name()] = new ConditionTracker(condition);
-    }
-
-    // Build MetricProducers for each metric defined in config.
-    // (1) build CountMetricProducer
-    for (int i = 0; i < config.count_metric_size(); i++) {
-        const CountMetric& metric = config.count_metric(i);
-        auto it = mLogMatchers.find(metric.what());
-        if (it == mLogMatchers.end()) {
-            ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
-            continue;
-        }
-
-        if (metric.has_condition()) {
-            auto condition_it = conditionMap.find(metric.condition());
-            if (condition_it == conditionMap.end()) {
-                ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
-                continue;
-            }
-            it->second.push_back(make_unique<CountMetricProducer>(metric, condition_it->second));
-        } else {
-            it->second.push_back(make_unique<CountMetricProducer>(metric));
-        }
-    }
-
-    // TODO: build other types of metrics too.
+MetricsManager::MetricsManager(const StatsdConfig& config) {
+    mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+                                    mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
+                                    mTrackerToConditionMap);
 }
 
 MetricsManager::~MetricsManager() {
     VLOG("~MetricManager()");
 }
 
+bool MetricsManager::isConfigValid() const {
+    return mConfigValid;
+}
+
 void MetricsManager::finish() {
-    for (auto const& entryPair : mLogMatchers) {
-        for (auto const& metric : entryPair.second) {
-            metric->finish();
-        }
+    for (auto& metricProducer : mAllMetricProducers) {
+        metricProducer->finish();
     }
 }
 
 // Consume the stats log if it's interesting to this metric.
 void MetricsManager::onLogEvent(const log_msg& logMsg) {
+    if (!mConfigValid) {
+        return;
+    }
+
     int tagId = getTagId(logMsg);
     if (mTagIds.find(tagId) == mTagIds.end()) {
         // not interesting...
         return;
     }
-    // Since at least one of the metrics is interested in this event, we parse it now.
-    LogEventWrapper event = LogEntryMatcherManager::parseLogEvent(logMsg);
 
-    // Evaluate the conditions. Order matters, this should happen
-    // before sending the event to metrics
-    for (auto& condition : mConditionTracker) {
-        condition->evaluateCondition(event);
+    // Since at least one of the metrics is interested in this event, we parse it now.
+    LogEventWrapper event = parseLogEvent(logMsg);
+    vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
+
+    for (auto& matcher : mAllLogEntryMatchers) {
+        matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache);
     }
 
-    // Now find out which LogMatcher matches this event, and let relevant metrics know.
-    for (auto matcher : mMatchers) {
-        if (LogEntryMatcherManager::matches(matcher, event)) {
-            auto it = mLogMatchers.find(matcher.name());
-            if (it != mLogMatchers.end()) {
-                for (auto const& it2 : it->second) {
-                    // Only metrics that matches this event get notified.
-                    it2->onMatchedLogEvent(event);
+    // A bitmap to see which ConditionTracker needs to be re-evaluated.
+    vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
+
+    for (const auto& pair : mTrackerToConditionMap) {
+        if (matcherCache[pair.first] == MatchingState::kMatched) {
+            const auto& conditionList = pair.second;
+            for (const int conditionIndex : conditionList) {
+                conditionToBeEvaluated[conditionIndex] = true;
+            }
+        }
+    }
+
+    vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
+                                          ConditionState::kNotEvaluated);
+    // A bitmap to track if a condition has changed value.
+    vector<bool> changedCache(mAllConditionTrackers.size(), false);
+    for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
+        if (conditionToBeEvaluated[i] == false) {
+            continue;
+        }
+
+        sp<ConditionTracker>& condition = mAllConditionTrackers[i];
+        condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
+                                     changedCache);
+        if (changedCache[i]) {
+            auto pair = mConditionToMetricMap.find(i);
+            if (pair != mConditionToMetricMap.end()) {
+                auto& metricList = pair->second;
+                for (auto metricIndex : metricList) {
+                    mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]);
                 }
-            } else {
-                // TODO: we should remove any redundant matchers that the config provides.
-                ALOGW("Matcher not used by any metrics.");
+            }
+        }
+    }
+
+    // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
+    for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
+        if (matcherCache[i] == MatchingState::kMatched) {
+            auto pair = mTrackerToMetricMap.find(i);
+            if (pair != mTrackerToMetricMap.end()) {
+                auto& metricList = pair->second;
+                for (const int metricIndex : metricList) {
+                    mAllMetricProducers[metricIndex]->onMatchedLogEvent(event);
+                }
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 77d7535..70c34db 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -20,8 +20,8 @@
 #include <cutils/log.h>
 #include <log/logprint.h>
 #include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
-#include "ConditionTracker.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
@@ -36,25 +36,58 @@
 
     ~MetricsManager();
 
-    // Consume the stats log if it's interesting to this metric.
+    // Return whether the configuration is valid.
+    bool isConfigValid() const;
+
     void onLogEvent(const log_msg& logMsg);
 
+    // Called when everything should wrap up. We are about to finish (e.g., new config comes).
     void finish();
 
 private:
-    const StatsdConfig mConfig;
-
     // All event tags that are interesting to my metrics.
     std::set<int> mTagIds;
 
-    // The matchers that my metrics share.
-    std::vector<LogEntryMatcher> mMatchers;
+    // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
+    // MetricManager. There are relationship between them, and the relationship are denoted by index
+    // instead of poiters. The reasons for this are: (1) the relationship between them are
+    // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds
+    // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the
+    // related results from a cache using the index.
+    // TODO: using unique_ptr may be more appriopreate?
 
-    // The conditions that my metrics share.
-    std::vector<sp<ConditionTracker>> mConditionTracker;
+    // Hold all the log entry matchers from the config.
+    std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
 
-    // the map from LogEntryMatcher names to the metrics that use this matcher.
-    std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers;
+    // Hold all the conditions from the config.
+    std::vector<sp<ConditionTracker>> mAllConditionTrackers;
+
+    // Hold all metrics from the config.
+    std::vector<sp<MetricProducer>> mAllMetricProducers;
+
+    // To make the log processing more efficient, we want to do as much filtering as possible
+    // before we go into individual trackers and conditions to match.
+
+    // 1st filter: check if the event tag id is in mTagIds.
+    // 2nd filter: if it is, we parse the event because there is at least one member is interested.
+    //             then pass to all LogMatchingTrackers (itself also filter events by ids).
+    // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the
+    //             ConditionTrackers and MetricProducers that use this matcher.
+    // 4th filter: for ConditionTrackers that changed value due to this event, we pass
+    //             new conditions to  metrics that use this condition.
+
+    // The following map is initialized from the statsd_config.
+
+    // maps from the index of the LogMatchingTracker to index of MetricProducer.
+    std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
+
+    // maps from LogMatchingTracker to ConditionTracker
+    std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
+
+    // maps from ConditionTracker to MetricProducer
+    std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
+
+    bool mConfigValid;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
new file mode 100644
index 0000000..6fdd228
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#include "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
+#include "CountMetricProducer.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
+                     vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) {
+    vector<LogEntryMatcher> matcherConfigs;
+
+    for (int i = 0; i < config.log_entry_matcher_size(); i++) {
+        const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
+
+        int index = allLogEntryMatchers.size();
+        switch (logMatcher.contents_case()) {
+            case LogEntryMatcher::ContentsCase::kSimpleLogEntryMatcher:
+                allLogEntryMatchers.push_back(new SimpleLogMatchingTracker(
+                        logMatcher.name(), index, logMatcher.simple_log_entry_matcher()));
+                break;
+            case LogEntryMatcher::ContentsCase::kCombination:
+                allLogEntryMatchers.push_back(
+                        new CombinationLogMatchingTracker(logMatcher.name(), index));
+                break;
+            default:
+                ALOGE("Matcher %s malformed", logMatcher.name().c_str());
+                return false;
+                // continue;
+        }
+        if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) {
+            ALOGE("Duplicate LogEntryMatcher found!");
+            return false;
+        }
+        logTrackerMap[logMatcher.name()] = index;
+        matcherConfigs.push_back(logMatcher);
+    }
+
+    vector<bool> stackTracker2(allLogEntryMatchers.size(), false);
+    for (auto& matcher : allLogEntryMatchers) {
+        if (!matcher->init(matcherConfigs, allLogEntryMatchers, logTrackerMap, stackTracker2)) {
+            return false;
+        }
+        // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+        const set<int>& tagIds = matcher->getTagIds();
+        allTagIds.insert(tagIds.begin(), tagIds.end());
+    }
+    return true;
+}
+
+bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+                    unordered_map<string, int>& conditionTrackerMap,
+                    vector<sp<ConditionTracker>>& allConditionTrackers,
+                    unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+    vector<Condition> conditionConfigs;
+
+    for (int i = 0; i < config.condition_size(); i++) {
+        const Condition& condition = config.condition(i);
+        int index = allConditionTrackers.size();
+        switch (condition.contents_case()) {
+            case Condition::ContentsCase::kSimpleCondition: {
+                allConditionTrackers.push_back(new SimpleConditionTracker(
+                        condition.name(), index, condition.simple_condition(), logTrackerMap));
+                break;
+            }
+            case Condition::ContentsCase::kCombination: {
+                allConditionTrackers.push_back(
+                        new CombinationConditionTracker(condition.name(), index));
+                break;
+            }
+            default:
+                ALOGE("Condition %s malformed", condition.name().c_str());
+                return false;
+        }
+        if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
+            ALOGE("Duplicate Condition found!");
+            return false;
+        }
+        conditionTrackerMap[condition.name()] = index;
+        conditionConfigs.push_back(condition);
+    }
+
+    vector<bool> stackTracker(allConditionTrackers.size(), false);
+    for (size_t i = 0; i < allConditionTrackers.size(); i++) {
+        auto& conditionTracker = allConditionTrackers[i];
+        if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
+                                    stackTracker)) {
+            return false;
+        }
+        for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
+            auto& conditionList = trackerToConditionMap[trackerIndex];
+            conditionList.push_back(i);
+        }
+    }
+    return true;
+}
+
+bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+                 const unordered_map<string, int>& conditionTrackerMap,
+                 vector<sp<MetricProducer>>& allMetricProducers,
+                 unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                 unordered_map<int, std::vector<int>>& trackerToMetricMap) {
+    // Build MetricProducers for each metric defined in config.
+    // (1) build CountMetricProducer
+    for (int i = 0; i < config.count_metric_size(); i++) {
+        const CountMetric& metric = config.count_metric(i);
+        if (!metric.has_what()) {
+            ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+            return false;
+        }
+
+        auto logTrackerIt = logTrackerMap.find(metric.what());
+        if (logTrackerIt == logTrackerMap.end()) {
+            ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
+            return false;
+        }
+
+        sp<MetricProducer> countProducer;
+        int metricIndex = allMetricProducers.size();
+        if (metric.has_condition()) {
+            auto condition_it = conditionTrackerMap.find(metric.condition());
+            if (condition_it == conditionTrackerMap.end()) {
+                ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
+                return false;
+            }
+            countProducer = new CountMetricProducer(metric, true /*has condition*/);
+            // will create new vector if not exist before.
+            auto& metricList = conditionToMetricMap[condition_it->second];
+            metricList.push_back(metricIndex);
+        } else {
+            countProducer = new CountMetricProducer(metric, false /*no condition*/);
+        }
+
+        int logTrackerIndex = logTrackerIt->second;
+        auto& metric_list = trackerToMetricMap[logTrackerIndex];
+        metric_list.push_back(metricIndex);
+        allMetricProducers.push_back(countProducer);
+    }
+
+    // TODO: build other types of metrics too.
+
+    return true;
+}
+
+bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
+                      vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                      vector<sp<ConditionTracker>>& allConditionTrackers,
+                      vector<sp<MetricProducer>>& allMetricProducers,
+                      unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                      unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                      unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+    unordered_map<string, int> logTrackerMap;
+    unordered_map<string, int> conditionTrackerMap;
+
+    if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
+        ALOGE("initLogMatchingTrackers failed");
+        return false;
+    }
+
+    if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
+                        trackerToConditionMap)) {
+        ALOGE("initConditionTrackers failed");
+        return false;
+    }
+
+    if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allMetricProducers,
+                     conditionToMetricMap, trackerToMetricMap)) {
+        ALOGE("initMetricProducers failed");
+        return false;
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
new file mode 100644
index 0000000..5f1f295
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -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.
+ */
+#ifndef METRIC_UTIL_H
+#define METRIC_UTIL_H
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
+#include "CountMetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to initialize from StatsdConfig.
+// *Note*: only initStatsdConfig() should be called from outside.
+// All other functions are intermediate
+// steps, created to make unit tests easier. And most of the parameters in these
+// functions are temporary objects in the initialization phase.
+
+// Initialize the LogMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// output:
+// [logTrackerMap]: this map should contain matcher name to index mapping
+// [allLogEntryMatchers]: should store the sp to all the LogMatchingTracker
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+bool initLogTrackers(const StatsdConfig& config,
+                     std::unordered_map<std::string, int>& logTrackerMap,
+                     std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                     std::set<int>& allTagIds);
+
+// Initialize ConditionTrackers
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// output:
+// [conditionTrackerMap]: this map should contain condition name to index mapping
+// [allConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contain the mapping from index of
+//                        log tracker to condition trackers that use the log tracker
+bool initConditions(const StatsdConfig& config,
+                    const std::unordered_map<std::string, int>& logTrackerMap,
+                    std::unordered_map<std::string, int>& conditionTrackerMap,
+                    std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                    std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+// Initialize MetricProducers.
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// [conditionTrackerMap]: condition name to index mapping
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+//                          the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool initMetrics(const StatsdConfig& config,
+                 const std::unordered_map<std::string, int>& logTrackerMap,
+                 const std::unordered_map<std::string, int>& conditionTrackerMap,
+                 std::vector<sp<MetricProducer>>& allMetricProducers,
+                 std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                 std::unordered_map<int, std::vector<int>>& trackerToMetricMap);
+
+// Initialize MetricManager from StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
+                      std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                      std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                      std::vector<sp<MetricProducer>>& allMetricProducers,
+                      std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                      std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // METRIC_UTIL_H
diff --git a/cmds/statsd/src/parse_util.cpp b/cmds/statsd/src/parse_util.cpp
deleted file mode 100644
index 61421880..0000000
--- a/cmds/statsd/src/parse_util.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/log_event_list.h>
-#include <parse_util.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static inline uint32_t get4LE(const char* src) {
-    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-int getTagId(log_msg msg) {
-    return get4LE(msg.msg());
-}
-
-EventMetricData parse(log_msg msg) {
-    // dump all statsd logs to dropbox for now.
-    // TODO: Add filtering, aggregation, etc.
-    EventMetricData eventMetricData;
-
-    // set tag.
-    int tag = getTagId(msg);
-    // TODO: Replace the following line when we can serialize on the fly.
-    //eventMetricData.set_tag(tag);
-
-    // set timestamp of the event.
-    eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
-
-    // start iterating k,v pairs.
-    android_log_context context =
-            create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
-                                      const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
-    android_log_list_element elem;
-
-    if (context) {
-        memset(&elem, 0, sizeof(elem));
-        size_t index = 0;
-        int32_t key = -1;
-
-        do {
-            elem = android_log_read_next(context);
-            switch ((int)elem.type) {
-                case EVENT_TYPE_INT:
-                    if (index % 2 == 0) {
-                        key = elem.data.int32;
-                    } else {
-                        // TODO: Fix the following lines when we can serialize on the fly.
-                        /*
-                        int32_t val = elem.data.int32;
-                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
-                        keyValuePair->set_key(key);
-                        keyValuePair->set_value_int(val);
-                        */
-                    }
-                    index++;
-                    break;
-                case EVENT_TYPE_FLOAT:
-                    if (index % 2 == 1) {
-                        // TODO: Fix the following lines when we can serialize on the fly.
-                        /*
-                        float val = elem.data.float32;
-                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
-                        keyValuePair->set_key(key);
-                        keyValuePair->set_value_float(val);
-                        */
-                    }
-                    index++;
-                    break;
-                case EVENT_TYPE_STRING:
-                    if (index % 2 == 1) {
-                        // TODO: Fix the following lines when we can serialize on the fly.
-                        /*
-                        char* val = elem.data.string;
-                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
-                        keyValuePair->set_key(key);
-                        keyValuePair->set_value_str(val);
-                        */
-                    }
-                    index++;
-                    break;
-                case EVENT_TYPE_LONG:
-                    if (index % 2 == 1) {
-                        // TODO: Fix the following lines when we can serialize on the fly.
-                        /*
-                        int64_t val = elem.data.int64;
-                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
-                        keyValuePair->set_key(key);
-                        keyValuePair->set_value_int(val);
-                        */
-                    }
-                    index++;
-                    break;
-                case EVENT_TYPE_LIST:
-                    break;
-                case EVENT_TYPE_LIST_STOP:
-                    break;
-                case EVENT_TYPE_UNKNOWN:
-                    break;
-                default:
-                    elem.complete = true;
-                    break;
-            }
-
-            if (elem.complete) {
-                break;
-            }
-        } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
-
-        android_log_destroy(&context);
-    }
-
-    return eventMetricData;
-}
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2dc0cc7..6421b70 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -55,6 +55,43 @@
   repeated CountBucketInfo bucket_info = 2;
 }
 
+message DurationBucketInfo {
+  optional int64 start_bucket_nanos = 1;
+
+  optional int64 end_bucket_nanos = 2;
+
+  optional int64 duration_nanos = 3;
+}
+
+message DurationMetricData {
+  repeated KeyValuePair dimension = 1;
+
+  repeated DurationBucketInfo bucket_info = 2;
+}
+
+message UidMapping {
+  message AppInfo {
+    optional string app = 1;
+
+    optional int32 version = 2;
+
+    optional int32 uid = 3;
+  }
+
+  repeated AppInfo initial = 1;
+
+  message Change {
+    optional bool deletion = 1;
+
+    optional int64 timestamp = 2;
+    optional string app = 3;
+    optional int32 uid = 4;
+
+    optional int32 version = 5;
+  }
+  repeated Change changes = 2;
+}
+
 message StatsLogReport {
   optional int32 metric_id = 1;
 
@@ -68,8 +105,12 @@
   message CountMetricDataWrapper {
     repeated CountMetricData data = 1;
   }
+  message DurationMetricDataWrapper {
+    repeated CountMetricData data = 1;
+  }
   oneof data {
     EventMetricDataWrapper event_metrics = 4;
     CountMetricDataWrapper count_metrics = 5;
+    DurationMetricDataWrapper duration_metrics = 6;
   }
 }
diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp
new file mode 100644
index 0000000..978b228
--- /dev/null
+++ b/cmds/statsd/src/stats_util.cpp
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+#include <log/log_event_list.h>
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static inline uint32_t get4LE(const char* src) {
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+int getTagId(log_msg msg) {
+    return get4LE(msg.msg());
+}
+
+EventMetricData parse(log_msg msg) {
+    // dump all statsd logs to dropbox for now.
+    // TODO: Add filtering, aggregation, etc.
+    EventMetricData eventMetricData;
+
+    // set tag.
+    int tag = getTagId(msg);
+    // TODO: Replace the following line when we can serialize on the fly.
+    // eventMetricData.set_tag(tag);
+
+    // set timestamp of the event.
+    eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
+
+    // start iterating k,v pairs.
+    android_log_context context =
+            create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
+                                      const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
+    android_log_list_element elem;
+
+    if (context) {
+        memset(&elem, 0, sizeof(elem));
+        size_t index = 0;
+        int32_t key = -1;
+
+        do {
+            elem = android_log_read_next(context);
+            switch ((int)elem.type) {
+                case EVENT_TYPE_INT:
+                    if (index % 2 == 0) {
+                        key = elem.data.int32;
+                    } else {
+                        // TODO: Fix the following lines when we can serialize on the fly.
+                        /*
+                        int32_t val = elem.data.int32;
+                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+                        keyValuePair->set_key(key);
+                        keyValuePair->set_value_int(val);
+                        */
+                    }
+                    index++;
+                    break;
+                case EVENT_TYPE_FLOAT:
+                    if (index % 2 == 1) {
+                        // TODO: Fix the following lines when we can serialize on the fly.
+                        /*
+                        float val = elem.data.float32;
+                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+                        keyValuePair->set_key(key);
+                        keyValuePair->set_value_float(val);
+                        */
+                    }
+                    index++;
+                    break;
+                case EVENT_TYPE_STRING:
+                    if (index % 2 == 1) {
+                        // TODO: Fix the following lines when we can serialize on the fly.
+                        /*
+                        char* val = elem.data.string;
+                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+                        keyValuePair->set_key(key);
+                        keyValuePair->set_value_str(val);
+                        */
+                    }
+                    index++;
+                    break;
+                case EVENT_TYPE_LONG:
+                    if (index % 2 == 1) {
+                        // TODO: Fix the following lines when we can serialize on the fly.
+                        /*
+                        int64_t val = elem.data.int64;
+                        KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+                        keyValuePair->set_key(key);
+                        keyValuePair->set_value_int(val);
+                        */
+                    }
+                    index++;
+                    break;
+                case EVENT_TYPE_LIST:
+                    break;
+                case EVENT_TYPE_LIST_STOP:
+                    break;
+                case EVENT_TYPE_UNKNOWN:
+                    break;
+                default:
+                    elem.complete = true;
+                    break;
+            }
+
+            if (elem.complete) {
+                break;
+            }
+        } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
+
+        android_log_destroy(&context);
+    }
+
+    return eventMetricData;
+}
+
+StatsdConfig buildFakeConfig() {
+    // HACK: Hard code a test metric for counting screen on events...
+    StatsdConfig config;
+    config.set_config_id(12345L);
+
+    // One count metric to count screen on
+    CountMetric* metric = config.add_count_metric();
+    metric->set_metric_id(20150717L);
+    metric->set_what("SCREEN_IS_ON");
+    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+
+    // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH
+    metric = config.add_count_metric();
+    metric->set_metric_id(20150718L);
+    metric->set_what("PHOTO_PROCESS_STATE_CHANGE");
+    metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+    metric->set_condition("SCREEN_IS_ON");
+
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_OFF");
+
+    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+
+
+    LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher();
+    procEventMatcher->set_name("PHOTO_CRASH");
+
+    SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+    KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                1002 /*pkg*/);
+    keyValueMatcher->set_eq_string(
+            "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                                   1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    keyValueMatcher->set_eq_int(2);
+
+
+    procEventMatcher = config.add_log_entry_matcher();
+    procEventMatcher->set_name("PHOTO_START");
+
+    simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+    keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                1002 /*pkg*/);
+    keyValueMatcher->set_eq_string(
+            "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                                   1 /*STATE*/);
+    keyValueMatcher->set_eq_int(1);
+
+
+    procEventMatcher = config.add_log_entry_matcher();
+    procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE");
+    LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination();
+    combinationMatcher->set_operation(LogicalOperation::OR);
+    combinationMatcher->add_matcher("PHOTO_START");
+    combinationMatcher->add_matcher("PHOTO_CRASH");
+
+
+    procEventMatcher = config.add_log_entry_matcher();
+    procEventMatcher->set_name("CHROME_CRASH");
+
+    simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+    keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                1002 /*pkg*/);
+    keyValueMatcher->set_eq_string(
+            "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+    keyValueMatcher->mutable_key_matcher()->set_key(
+                                   1 /*STATE*/);
+    keyValueMatcher->set_eq_int(2);
+
+
+
+    procEventMatcher = config.add_log_entry_matcher();
+    procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH");
+    combinationMatcher = procEventMatcher->mutable_combination();
+    combinationMatcher->set_operation(LogicalOperation::OR);
+    combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE");
+    combinationMatcher->add_matcher("CHROME_CRASH");
+
+
+
+    Condition* condition = config.add_condition();
+    condition->set_name("SCREEN_IS_ON");
+    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+    simpleCondition->set_start("SCREEN_IS_ON");
+    simpleCondition->set_stop("SCREEN_IS_OFF");
+
+
+    condition = config.add_condition();
+        condition->set_name("PHOTO_STARTED");
+
+        simpleCondition = condition->mutable_simple_condition();
+        simpleCondition->set_start("PHOTO_START");
+        simpleCondition->set_stop("PHOTO_CRASH");
+
+
+    condition = config.add_condition();
+    condition->set_name("SCREEN_IS_OFF");
+
+    simpleCondition = condition->mutable_simple_condition();
+    simpleCondition->set_start("SCREEN_IS_OFF");
+    simpleCondition->set_stop("SCREEN_IS_ON");
+
+
+    condition = config.add_condition();
+    condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+    Condition_Combination* combination = condition->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_condition("SCREEN_IS_ON");
+    combination->add_condition("SCREEN_IS_OFF");
+
+
+    condition = config.add_condition();
+    condition->set_name("SCREEN_IS_NEITHER_ON_OFF");
+
+    combination = condition->mutable_combination();
+    combination->set_operation(LogicalOperation::NOR);
+    combination->add_condition("SCREEN_IS_ON");
+    combination->add_condition("SCREEN_IS_OFF");
+
+    return config;
+}
+
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/stats_util.h
similarity index 90%
rename from cmds/statsd/src/parse_util.h
rename to cmds/statsd/src/stats_util.h
index 8b82e7b..25b9bba 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -20,6 +20,7 @@
 #include "LogReader.h"
 
 #include <log/logprint.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
 namespace os {
@@ -29,6 +30,8 @@
 
 int getTagId(log_msg msg);
 
+StatsdConfig buildFakeConfig();
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 3e4ebaf..d7702cd 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -65,7 +65,8 @@
 
   message Combination {
     optional LogicalOperation operation = 1;
-    repeated LogEntryMatcher matcher = 2;
+
+    repeated string matcher = 2;
   }
   oneof contents {
     SimpleLogEntryMatcher simple_log_entry_matcher = 2;
@@ -122,6 +123,24 @@
   optional Bucket bucket = 5;
 }
 
+message DurationMetric {
+  optional int64 metric_id = 1;
+
+  enum AggregationType {
+    DURATION_SUM = 1;
+
+    DURATION_MAX_SPARSE = 2;
+    DURATION_MIN_SPARSE = 3;
+  }
+  optional AggregationType type = 2;
+
+  optional string predicate = 3;
+
+  repeated KeyMatcher dimension = 4;
+
+  optional Bucket bucket = 5;
+}
+
 message StatsdConfig {
   optional int64 config_id = 1;
 
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp
new file mode 100644
index 0000000..d5b6811
--- /dev/null
+++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp
@@ -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.
+
+#define LOG_TAG "statsd_test"
+
+#include "../src/AnomalyMonitor.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+TEST(AnomalyMonitor, popSoonerThan) {
+    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
+    AnomalyMonitor am(2);
+
+    set = am.popSoonerThan(5);
+    EXPECT_TRUE(set.empty());
+
+    sp<const AnomalyAlarm> a = new AnomalyAlarm{10};
+    sp<const AnomalyAlarm> b = new AnomalyAlarm{20};
+    sp<const AnomalyAlarm> c = new AnomalyAlarm{20};
+    sp<const AnomalyAlarm> d = new AnomalyAlarm{30};
+    sp<const AnomalyAlarm> e = new AnomalyAlarm{40};
+    sp<const AnomalyAlarm> f = new AnomalyAlarm{50};
+
+    am.add(a);
+    am.add(b);
+    am.add(c);
+    am.add(d);
+    am.add(e);
+    am.add(f);
+
+    set = am.popSoonerThan(5);
+    EXPECT_TRUE(set.empty());
+
+    set = am.popSoonerThan(30);
+    EXPECT_EQ(4u, set.size());
+    EXPECT_EQ(1u, set.count(a));
+    EXPECT_EQ(1u, set.count(b));
+    EXPECT_EQ(1u, set.count(c));
+    EXPECT_EQ(1u, set.count(d));
+
+    set = am.popSoonerThan(60);
+    EXPECT_EQ(2u, set.size());
+    EXPECT_EQ(1u, set.count(e));
+    EXPECT_EQ(1u, set.count(f));
+
+    set = am.popSoonerThan(80);
+    EXPECT_EQ(0u, set.size());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/ConditionTracker_test.cpp
new file mode 100644
index 0000000..f8b0fd0
--- /dev/null
+++ b/cmds/statsd/tests/ConditionTracker_test.cpp
@@ -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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <vector>
+
+using namespace android::os::statsd;
+using std::vector;
+
+
+#ifdef __ANDROID__
+TEST(ConditionTrackerTest, TestUnknownCondition) {
+    LogicalOperation operation = LogicalOperation::AND;
+
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+    children.push_back(2);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kUnknown);
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kTrue);
+
+    EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
+            ConditionState::kUnknown);
+}
+TEST(ConditionTrackerTest, TestAndCondition) {
+    // Set up the matcher
+    LogicalOperation operation = LogicalOperation::AND;
+
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+    children.push_back(2);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kTrue);
+
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kTrue);
+
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestOrCondition) {
+    // Set up the matcher
+    LogicalOperation operation = LogicalOperation::OR;
+
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+    children.push_back(2);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kTrue);
+
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kFalse);
+
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNotCondition) {
+    // Set up the matcher
+    LogicalOperation operation = LogicalOperation::NOT;
+
+    vector<int> children;
+    children.push_back(0);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kTrue);
+
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kFalse);
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNandCondition) {
+    // Set up the matcher
+    LogicalOperation operation = LogicalOperation::NAND;
+
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kFalse);
+
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kFalse);
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kTrue);
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNorCondition) {
+    // Set up the matcher
+    LogicalOperation operation = LogicalOperation::NOR;
+
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+
+    vector<ConditionState> conditionResults;
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kFalse);
+
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kFalse);
+    conditionResults.push_back(ConditionState::kFalse);
+    EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+    conditionResults.clear();
+    conditionResults.push_back(ConditionState::kTrue);
+    conditionResults.push_back(ConditionState::kTrue);
+    EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 473704a..6069801 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -18,14 +18,15 @@
 #include <log/log_event_list.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
-#include "../src/matchers/LogEntryMatcherManager.h"
-#include "../src/parse_util.h"
+#include "../src/matchers/matcher_util.h"
+#include "../src/stats_util.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 #include <stdio.h>
 
 using namespace android::os::statsd;
 using std::unordered_map;
+using std::vector;
 
 const int kTagIdWakelock = 123;
 const int kKeyIdState = 45;
@@ -41,7 +42,7 @@
     LogEventWrapper wrapper;
     wrapper.tagId = kTagIdWakelock;
 
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 TEST(LogEntryMatcherTest, TestBoolMatcher) {
@@ -57,13 +58,13 @@
 
     keyValue->set_eq_bool(true);
     wrapper.boolMap[kKeyIdState] = true;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 
     keyValue->set_eq_bool(false);
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
 
-    wrapper.boolMap[kTagIdWakelock] = false;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    wrapper.boolMap[kKeyIdState] = false;
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 TEST(LogEntryMatcherTest, TestStringMatcher) {
@@ -80,7 +81,7 @@
 
     wrapper.strMap[kKeyIdState] = "wakelock_name";
 
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
@@ -96,19 +97,19 @@
 
     keyValue->set_lt_int(10);
     wrapper.intMap[kKeyIdState] = 11;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 10;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 9;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 
     keyValue->set_gt_int(10);
     wrapper.intMap[kKeyIdState] = 11;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 10;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 9;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) {
@@ -124,19 +125,19 @@
 
     keyValue->set_lte_int(10);
     wrapper.intMap[kKeyIdState] = 11;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 10;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 9;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 
     keyValue->set_gte_int(10);
     wrapper.intMap[kKeyIdState] = 11;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 10;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.intMap[kKeyIdState] = 9;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
@@ -152,15 +153,15 @@
 
     keyValue->set_lt_float(10.0);
     wrapper.floatMap[kKeyIdState] = 10.1;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.floatMap[kKeyIdState] = 9.9;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
 
     keyValue->set_gt_float(10.0);
     wrapper.floatMap[kKeyIdState] = 10.1;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
     wrapper.floatMap[kKeyIdState] = 9.9;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
 }
 
 // Helper for the composite matchers.
@@ -173,141 +174,117 @@
 
 TEST(LogEntryMatcherTest, TestAndMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto combination = matcher.mutable_combination();
-    combination->set_operation(LogicalOperation::AND);
+    LogicalOperation operation = LogicalOperation::AND;
 
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-                     kTagIdWakelock, kKeyIdState, 3);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-                     kTagIdWakelock, kKeyIdPackageVersion, 4);
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+    children.push_back(2);
 
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
+    vector<MatchingState> matcherResults;
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kMatched);
 
-    wrapper.intMap[1003] = 4;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap.clear();
-    wrapper.intMap[1] = 3;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap.clear();
-    wrapper.intMap[1] = 3;
-    wrapper.intMap[1003] = 4;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kMatched);
+
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
 }
 
 TEST(LogEntryMatcherTest, TestOrMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto combination = matcher.mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
+    LogicalOperation operation = LogicalOperation::OR;
 
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdState, 3);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdPackageVersion, 4);
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
+    children.push_back(2);
 
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
+    vector<MatchingState> matcherResults;
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kMatched);
 
-    // Don't set any key-value pairs.
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[1003] = 4;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap.clear();
-    wrapper.intMap[1] = 3;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap.clear();
-    wrapper.intMap[1] = 3;
-    wrapper.intMap[1003] = 4;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
 
 TEST(LogEntryMatcherTest, TestNotMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto combination = matcher.mutable_combination();
-    combination->set_operation(LogicalOperation::NOT);
+    LogicalOperation operation = LogicalOperation::NOT;
 
-    // Define first simpleMatcher
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdState, 3);
+    vector<int> children;
+    children.push_back(0);
 
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
+    vector<MatchingState> matcherResults;
+    matcherResults.push_back(MatchingState::kMatched);
 
-    // Don't set any key-value pairs.
-    wrapper.intMap[kKeyIdState] = 3;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kNotMatched);
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestNANDMatcher) {
+TEST(LogEntryMatcherTest, TestNandMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto combination = matcher.mutable_combination();
-    combination->set_operation(LogicalOperation::NAND);
+    LogicalOperation operation = LogicalOperation::NAND;
 
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdState, 3);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdPackageVersion, 4);
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
 
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
+    vector<MatchingState> matcherResults;
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
 
-    // Don't set any key-value pairs.
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdState] = 3;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdPackageVersion] = 4;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kMatched);
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestNORMatcher) {
+TEST(LogEntryMatcherTest, TestNorMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto combination = matcher.mutable_combination();
-    combination->set_operation(LogicalOperation::NOR);
+    LogicalOperation operation = LogicalOperation::NOR;
 
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdState, 3);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdPackageVersion, 4);
+    vector<int> children;
+    children.push_back(0);
+    children.push_back(1);
 
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
+    vector<MatchingState> matcherResults;
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
 
-    // Don't set any key-value pairs.
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdState] = 3;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdPackageVersion] = 4;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-}
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 
-// Tests that a NOT on top of AND is the same as NAND
-TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) {
-    LogEntryMatcher matcher;
-    auto not_combination = matcher.mutable_combination();
-    not_combination->set_operation(LogicalOperation::NOT);
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kNotMatched);
+    matcherResults.push_back(MatchingState::kNotMatched);
+    EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
 
-    // Now add the AND
-    auto combination = not_combination->add_matcher()->mutable_combination();
-    combination->set_operation(LogicalOperation::AND);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdState, 3);
-    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
-        kTagIdWakelock, kKeyIdPackageVersion, 4);
-
-    LogEventWrapper wrapper;
-    wrapper.tagId = kTagIdWakelock;
-
-    // Don't set any key-value pairs.
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdState] = 3;
-    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
-    wrapper.intMap[kKeyIdPackageVersion] = 4;
-    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+    matcherResults.clear();
+    matcherResults.push_back(MatchingState::kMatched);
+    matcherResults.push_back(MatchingState::kMatched);
+    EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
 
 #else
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
new file mode 100644
index 0000000..673c156
--- /dev/null
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -0,0 +1,231 @@
+// 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 "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/ConditionTracker.h"
+#include "../src/matchers/LogMatchingTracker.h"
+#include "../src/metrics/CountMetricProducer.h"
+#include "../src/metrics/MetricProducer.h"
+#include "../src/metrics/metrics_manager_util.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+// TODO: ADD MORE TEST CASES.
+
+StatsdConfig buildGoodConfig() {
+    StatsdConfig config;
+    config.set_config_id(12345L);
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_OFF");
+
+    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher("SCREEN_IS_ON");
+    combination->add_matcher("SCREEN_IS_OFF");
+
+    return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+    StatsdConfig config;
+    config.set_config_id(12345L);
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher("SCREEN_IS_ON");
+    // Circle dependency
+    combination->add_matcher("SCREEN_ON_OR_OFF");
+
+    return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+    StatsdConfig config;
+    config.set_config_id(12345L);
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher("SCREEN_IS_ON");
+    // undefined matcher
+    combination->add_matcher("ABC");
+
+    return config;
+}
+
+StatsdConfig buildCircleConditions() {
+    StatsdConfig config;
+    config.set_config_id(12345L);
+
+    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_ON");
+
+    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher->set_name("SCREEN_IS_OFF");
+
+    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+    simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    Condition* condition = config.add_condition();
+    condition->set_name("SCREEN_IS_ON");
+    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+    simpleCondition->set_start("SCREEN_IS_ON");
+    simpleCondition->set_stop("SCREEN_IS_OFF");
+
+    condition = config.add_condition();
+    condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+    Condition_Combination* combination = condition->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_condition("SCREEN_IS_ON");
+    combination->add_condition("SCREEN_IS_EITHER_ON_OFF");
+
+    return config;
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+    StatsdConfig config = buildGoodConfig();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+    EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+                                 allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+                                 trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+    StatsdConfig config = buildCircleMatchers();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+                                  trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+    StatsdConfig config = buildMissingMatchers();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+                                  trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleConditionDependency) {
+    StatsdConfig config = buildCircleConditions();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+                                  allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+                                  trackerToConditionMap));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
new file mode 100644
index 0000000..b6f1449
--- /dev/null
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/UidMap.h"
+#include <stdio.h>
+
+using namespace android;
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+const string kApp1 = "app1.sharing.1";
+const string kApp2 = "app2.sharing.1";
+
+TEST(UidMapTest, TestMatching) {
+    UidMap m;
+    vector<int32_t> uids;
+    vector<int32_t> versions;
+    vector<String16> apps;
+
+    uids.push_back(1000);
+    uids.push_back(1000);
+    apps.push_back(String16(kApp1.c_str()));
+    apps.push_back(String16(kApp2.c_str()));
+    versions.push_back(4);
+    versions.push_back(5);
+    m.updateMap(uids, versions, apps);
+    EXPECT_TRUE(m.hasApp(1000, kApp1));
+    EXPECT_TRUE(m.hasApp(1000, kApp2));
+    EXPECT_FALSE(m.hasApp(1000, "not.app"));
+}
+
+TEST(UidMapTest, TestAddAndRemove) {
+    UidMap m;
+    vector<int32_t> uids;
+    vector<int32_t> versions;
+    vector<String16> apps;
+
+    uids.push_back(1000);
+    uids.push_back(1000);
+    apps.push_back(String16(kApp1.c_str()));
+    apps.push_back(String16(kApp2.c_str()));
+    versions.push_back(4);
+    versions.push_back(5);
+    m.updateMap(uids, versions, apps);
+
+    m.updateApp(String16(kApp1.c_str()), 1000, 40);
+    EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
+
+    m.removeApp(String16(kApp1.c_str()), 1000);
+    EXPECT_FALSE(m.hasApp(1000, kApp1));
+    EXPECT_TRUE(m.hasApp(1000, kApp2));
+}
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
\ No newline at end of file
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index e4d4d25..74a482e 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -182,6 +182,40 @@
     EXPECT_FALSE(ipq.contains(nullptr));
 }
 
+TEST(indexed_priority_queue, pop) {
+    indexed_priority_queue<AATest, AATest::Smaller> ipq;
+    sp<const AATest> a = new AATest{1};
+    sp<const AATest> b = new AATest{2};
+    sp<const AATest> c = new AATest{3};
+
+    ipq.push(c);
+    ipq.push(b);
+    ipq.push(a);
+    EXPECT_EQ(3u, ipq.size());
+
+    ipq.pop();
+    EXPECT_EQ(2u, ipq.size());
+    EXPECT_FALSE(ipq.contains(a));
+    EXPECT_TRUE(ipq.contains(b));
+    EXPECT_TRUE(ipq.contains(c));
+
+    ipq.pop();
+    EXPECT_EQ(1u, ipq.size());
+    EXPECT_FALSE(ipq.contains(a));
+    EXPECT_FALSE(ipq.contains(b));
+    EXPECT_TRUE(ipq.contains(c));
+
+    ipq.pop();
+    EXPECT_EQ(0u, ipq.size());
+    EXPECT_FALSE(ipq.contains(a));
+    EXPECT_FALSE(ipq.contains(b));
+    EXPECT_FALSE(ipq.contains(c));
+    EXPECT_TRUE(ipq.empty());
+
+    ipq.pop(); // pop an empty queue
+    EXPECT_TRUE(ipq.empty());
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5e61727..dff43ed 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,13 +16,6 @@
 
 package android.app;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -670,138 +663,6 @@
         /** Invalid stack ID. */
         public static final int INVALID_STACK_ID = -1;
 
-        /** First static stack ID.
-         * @hide */
-        private  static final int FIRST_STATIC_STACK_ID = 0;
-
-        /** ID of stack where fullscreen activities are normally launched into.
-         * @hide */
-        public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
-        /** ID of stack where freeform/resized activities are normally launched into.
-         * @hide */
-        public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
-        /** ID of stack that occupies a dedicated region of the screen.
-         * @hide */
-        public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
-        /** ID of stack that always on top (always visible) when it exist.
-         * @hide */
-        public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
-
-        /** Last static stack stack ID.
-         * @hide */
-        private static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
-
-        /** Start of ID range used by stacks that are created dynamically.
-         * @hide */
-        public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
-
-        // TODO: Figure-out a way to remove this.
-        /** @hide */
-        public static boolean isStaticStack(int stackId) {
-            return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
-        }
-
-        // TODO: It seems this mostly means a stack on a secondary display now. Need to see if
-        // there are other meanings. If not why not just use information from the display?
-        /** @hide */
-        public static boolean isDynamicStack(int stackId) {
-            return stackId >= FIRST_DYNAMIC_STACK_ID;
-        }
-
-        /**
-         * Returns true if we try to maintain focus in the current stack when the top activity
-         * finishes.
-         * @hide
-         */
-        // TODO: Figure-out a way to remove. Probably isn't needed in the new world...
-        public static boolean keepFocusInStackIfPossible(int stackId) {
-            return stackId == FREEFORM_WORKSPACE_STACK_ID
-                    || stackId == DOCKED_STACK_ID || 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.
-         * @hide
-         */
-        public static boolean replaceWindowsOnTaskMove(int sourceStackId, int targetStackId) {
-            return sourceStackId == FREEFORM_WORKSPACE_STACK_ID
-                    || targetStackId == FREEFORM_WORKSPACE_STACK_ID;
-        }
-
-        /**
-         * Returns true if the top task in the task is allowed to return home when finished and
-         * there are other tasks in the stack.
-         * @hide
-         */
-        public static boolean allowTopTaskToReturnHome(int stackId) {
-            return stackId != PINNED_STACK_ID;
-        }
-
-        /**
-         * Returns true if the stack should be resized to match the bounds specified by
-         * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
-         * @hide
-         */
-        public static boolean resizeStackWithLaunchBounds(int stackId) {
-            return stackId == PINNED_STACK_ID;
-        }
-
-        /**
-         * Returns true if a window from the specified stack with {@param stackId} are normally
-         * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
-         * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
-         * etc.
-         * @hide
-         */
-        // TODO: What about the other side of docked stack if we move this to WindowConfiguration?
-        public static boolean normallyFullscreenWindows(int stackId) {
-            return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
-                    && stackId != DOCKED_STACK_ID;
-        }
-
-        /** Returns the stack id for the input windowing mode.
-         * @hide */
-        // TODO: To be removed once we are not using stack id for stuff...
-        public static int getStackIdForWindowingMode(int windowingMode) {
-            switch (windowingMode) {
-                case WINDOWING_MODE_PINNED: return PINNED_STACK_ID;
-                case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID;
-                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID;
-                case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return FULLSCREEN_WORKSPACE_STACK_ID;
-                case WINDOWING_MODE_FULLSCREEN: return FULLSCREEN_WORKSPACE_STACK_ID;
-                default: return INVALID_STACK_ID;
-            }
-        }
-
-        /** Returns the windowing mode that should be used for this input stack id.
-         * @hide */
-        // TODO: To be removed once we are not using stack id for stuff...
-        public static int getWindowingModeForStackId(int stackId, boolean inSplitScreenMode) {
-            final int windowingMode;
-            switch (stackId) {
-                case FULLSCREEN_WORKSPACE_STACK_ID:
-                    windowingMode = inSplitScreenMode
-                            ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN;
-                    break;
-                case PINNED_STACK_ID:
-                    windowingMode = WINDOWING_MODE_PINNED;
-                    break;
-                case DOCKED_STACK_ID:
-                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-                    break;
-                case FREEFORM_WORKSPACE_STACK_ID:
-                    windowingMode = WINDOWING_MODE_FREEFORM;
-                    break;
-                default :
-                    windowingMode = WINDOWING_MODE_UNDEFINED;
-            }
-            return windowingMode;
-        }
     }
 
     /**
@@ -1571,7 +1432,6 @@
             }
             dest.writeInt(stackId);
             dest.writeInt(userId);
-            dest.writeLong(firstActiveTime);
             dest.writeLong(lastActiveTime);
             dest.writeInt(affiliatedTaskId);
             dest.writeInt(affiliatedTaskColor);
@@ -1600,7 +1460,6 @@
                     TaskDescription.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
-            firstActiveTime = source.readLong();
             lastActiveTime = source.readLong();
             affiliatedTaskId = source.readInt();
             affiliatedTaskColor = source.readInt();
@@ -1643,31 +1502,6 @@
     public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
 
     /**
-     * Provides a list that contains recent tasks for all
-     * profiles of a user.
-     * @hide
-     */
-    public static final int RECENT_INCLUDE_PROFILES = 0x0004;
-
-    /**
-     * Ignores all tasks that are on the home stack.
-     * @hide
-     */
-    public static final int RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS = 0x0008;
-
-    /**
-     * Ignores the top task in the docked stack.
-     * @hide
-     */
-    public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010;
-
-    /**
-     * Ignores all tasks that are on the pinned stack.
-     * @hide
-     */
-    public static final int RECENT_INGORE_PINNED_STACK_TASKS = 0x0020;
-
-    /**
      * <p></p>Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      *
@@ -1702,33 +1536,7 @@
     public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
             throws SecurityException {
         try {
-            return getService().getRecentTasks(maxNum,
-                    flags, UserHandle.myUserId()).getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Same as {@link #getRecentTasks(int, int)} but returns the recent tasks for a
-     * specific user. It requires holding
-     * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
-     * @param maxNum The maximum number of entries to return in the list.  The
-     * actual number returned may be smaller, depending on how many tasks the
-     * user has started and the maximum number the system can remember.
-     * @param flags Information about what to return.  May be any combination
-     * of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}.
-     *
-     * @return Returns a list of RecentTaskInfo records describing each of
-     * the recent tasks. Most recently activated tasks go first.
-     *
-     * @hide
-     */
-    public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId)
-            throws SecurityException {
-        try {
-            return getService().getRecentTasks(maxNum,
-                    flags, userId).getList();
+            return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a68c3a5..f14831d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f039516..117854a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -178,8 +178,8 @@
      * SIGUSR1 is delivered. All others are ignored.
      */
     void signalPersistentProcesses(int signal);
-    ParceledListSlice getRecentTasks(int maxNum,
-            int flags, int userId);
+
+    ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
     oneway void activityDestroyed(in IBinder token);
     IIntentSender getIntentSender(int type, in String packageName, in IBinder token,
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 54f74b1..1fe2900 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -387,8 +387,6 @@
      * such as the Home key and the right soft keys, don't work.
      *
      * @return true if in keyguard restricted input mode.
-     *
-     * @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode
      */
     public boolean inKeyguardRestrictedInputMode() {
         try {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 47063f0..c06ad3f 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -32,6 +32,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import com.android.internal.util.Preconditions;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eb52cb7..a52dc1e 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -934,8 +934,14 @@
         public static final int PRIORITY_CATEGORY_CALLS = 1 << 3;
         /** Calls from repeat callers are prioritized. */
         public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4;
+        /** Alarms are prioritized */
+        public static final int PRIORITY_CATEGORY_ALARMS = 1 << 5;
+        /** Media, system, game (catch-all for non-never suppressible sounds) are prioritized */
+        public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 1 << 6;
 
         private static final int[] ALL_PRIORITY_CATEGORIES = {
+            PRIORITY_CATEGORY_ALARMS,
+            PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
             PRIORITY_CATEGORY_REMINDERS,
             PRIORITY_CATEGORY_EVENTS,
             PRIORITY_CATEGORY_MESSAGES,
@@ -1135,6 +1141,9 @@
                 case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES";
                 case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS";
                 case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS";
+                case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS";
+                case PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER:
+                    return "PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER";
                 default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
             }
         }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 8987bc0..23c4166 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -73,15 +73,16 @@
     public static final int DISABLE2_QUICK_SETTINGS = 1;
     public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
     public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
+    public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
 
     public static final int DISABLE2_NONE = 0x00000000;
 
     public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
-            | DISABLE2_NOTIFICATION_SHADE;
+            | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
 
     @IntDef(flag = true,
             value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
-                    DISABLE2_NOTIFICATION_SHADE})
+                    DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Disable2Flags {}
 
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40538..251863c 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -511,7 +511,8 @@
         return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
     }
 
-    private static String windowingModeToString(@WindowingMode int windowingMode) {
+    /** @hide */
+    public static String windowingModeToString(@WindowingMode int windowingMode) {
         switch (windowingMode) {
             case WINDOWING_MODE_UNDEFINED: return "undefined";
             case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index 3868439..0deb2e1 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -24,7 +24,6 @@
 import android.annotation.SystemService;
 import android.content.ClipData;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 
@@ -40,16 +39,18 @@
  * and how to construct them. You will construct these JobInfo objects and pass them to the
  * JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the
  * system will execute this job on your application's {@link android.app.job.JobService}.
- * You identify which JobService is meant to execute the logic for your job when you create the
- * JobInfo with
+ * You identify the service component that implements the logic for your job when you
+ * construct the JobInfo using
  * {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.
  * </p>
  * <p>
- * The framework will be intelligent about when you receive your callbacks, and attempt to batch
- * and defer them as much as possible. Typically if you don't specify a deadline on your job, it
- * can be run at any moment depending on the current state of the JobScheduler's internal queue,
- * however it might be deferred as long as until the next time the device is connected to a power
- * source.
+ * The framework will be intelligent about when it executes jobs, and attempt to batch
+ * and defer them as much as possible. Typically if you don't specify a deadline on a job, it
+ * can be run at any moment depending on the current state of the JobScheduler's internal queue.
+ * <p>
+ * While a job is running, the system holds a wakelock on behalf of your app.  For this reason,
+ * you do not need to take any action to guarantee that the device stays awake for the
+ * duration of the job.
  * </p>
  * <p>You do not
  * instantiate this class directly; instead, retrieve it through
@@ -141,30 +142,34 @@
             int userId, String tag);
 
     /**
-     * Cancel a job that is pending in the JobScheduler.
-     * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
-     * {@link #getAllPendingJobs()}.
+     * Cancel the specified job.  If the job is currently executing, it is stopped
+     * immediately and the return value from its {@link JobService#onStopJob(JobParameters)}
+     * method is ignored.
+     *
+     * @param jobId unique identifier for the job to be canceled, as supplied to
+     *     {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName)
+     *     JobInfo.Builder(int, android.content.ComponentName)}.
      */
     public abstract void cancel(int jobId);
 
     /**
-     * Cancel all jobs that have been registered with the JobScheduler by this package.
+     * Cancel <em>all</em> jobs that have been scheduled by the calling application.
      */
     public abstract void cancelAll();
 
     /**
-     * Retrieve all jobs for this package that are pending in the JobScheduler.
+     * Retrieve all jobs that have been scheduled by the calling application.
      *
-     * @return a list of all the jobs registered by this package that have not
-     *         yet been executed.
+     * @return a list of all of the app's scheduled jobs.  This includes jobs that are
+     *     currently started as well as those that are still waiting to run.
      */
     public abstract @NonNull List<JobInfo> getAllPendingJobs();
 
     /**
-     * Retrieve a specific job for this package that is pending in the
-     * JobScheduler.
+     * Look up the description of a scheduled job.
      *
-     * @return job registered by this package that has not yet been executed.
+     * @return The {@link JobInfo} description of the given scheduled job, or {@code null}
+     *     if the supplied job ID does not correspond to any job.
      */
     public abstract @Nullable JobInfo getPendingJob(int jobId);
 }
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 9096b47..69afed2 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -18,16 +18,7 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.ref.WeakReference;
 
 /**
  * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
@@ -55,7 +46,7 @@
      * </pre>
      *
      * <p>If a job service is declared in the manifest but not protected with this
-     * permission, that service will be ignored by the OS.
+     * permission, that service will be ignored by the system.
      */
     public static final String PERMISSION_BIND =
             "android.permission.BIND_JOB_SERVICE";
@@ -81,14 +72,36 @@
     }
 
     /**
-     * Override this method with the callback logic for your job. Any such logic needs to be
-     * performed on a separate thread, as this function is executed on your application's main
-     * thread.
+     * Called to indicate that the job has begun executing.  Override this method with the
+     * logic for your job.  Like all other component lifecycle callbacks, this method executes
+     * on your application's main thread.
+     * <p>
+     * Return {@code true} from this method if your job needs to continue running.  If you
+     * do this, the job remains active until you call
+     * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed
+     * its work, or until the job's required constraints are no longer satisfied.  For
+     * example, if the job was scheduled using
+     * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)},
+     * it will be immediately halted by the system if the user unplugs the device from power,
+     * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app
+     * will be expected to shut down all ongoing work connected with that job.
+     * <p>
+     * The system holds a wakelock on behalf of your app as long as your job is executing.
+     * This wakelock is acquired before this method is invoked, and is not released until either
+     * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes
+     * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down
+     * prematurely.
+     * <p>
+     * Returning {@code false} from this method means your job is already finished.  The
+     * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)}
+     * will not be invoked.
      *
-     * @param params Parameters specifying info about this job, including the extras bundle you
-     *               optionally provided at job-creation time.
-     * @return True if your service needs to process the work (on a separate thread). False if
-     * there's no more work to be done for this job.
+     * @param params Parameters specifying info about this job, including the optional
+     *     extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
+     *     This object serves to identify this specific running job instance when calling
+     *     {@link #jobFinished(JobParameters, boolean)}.
+     * @return {@code true} if your service will continue running, using a separate thread
+     *     when appropriate.  {@code false} means that this job has completed its work.
      */
     public abstract boolean onStartJob(JobParameters params);
 
@@ -101,37 +114,44 @@
      * {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
      * job was executing the user toggled WiFi. Another example is if you had specified
      * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
-     * idle maintenance window. You are solely responsible for the behaviour of your application
-     * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
-     * immediate repercussion is that the system will cease holding a wakelock for you.</p>
+     * idle maintenance window. You are solely responsible for the behavior of your application
+     * upon receipt of this message; your app will likely start to misbehave if you ignore it.
+     * <p>
+     * Once this method returns, the system releases the wakelock that it is holding on
+     * behalf of the job.</p>
      *
-     * @param params Parameters specifying info about this job.
-     * @return True to indicate to the JobManager whether you'd like to reschedule this job based
-     * on the retry criteria provided at job creation-time. False to drop the job. Regardless of
-     * the value returned, your job must stop executing.
+     * @param params The parameters identifying this job, as supplied to
+     *               the job in the {@link #onStartJob(JobParameters)} callback.
+     * @return {@code true} to indicate to the JobManager whether you'd like to reschedule
+     * this job based on the retry criteria provided at job creation-time; or {@code false}
+     * to end the job entirely.  Regardless of the value returned, your job must stop executing.
      */
     public abstract boolean onStopJob(JobParameters params);
 
     /**
-     * Call this to inform the JobManager you've finished executing. This can be called from any
-     * thread, as it will ultimately be run on your application's main thread. When the system
-     * receives this message it will release the wakelock being held.
+     * Call this to inform the JobScheduler that the job has finished its work.  When the
+     * system receives this message, it releases the wakelock being held for the job.
      * <p>
-     *     You can specify post-execution behaviour to the scheduler here with
-     *     <code>needsReschedule </code>. This will apply a back-off timer to your job based on
-     *     the default, or what was set with
-     *     {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original
-     *     requirements are always honoured even for a backed-off job. Note that a job running in
-     *     idle mode will not be backed-off. Instead what will happen is the job will be re-added
-     *     to the queue and re-executed within a future idle maintenance window.
+     * You can request that the job be scheduled again by passing {@code true} as
+     * the <code>wantsReschedule</code> parameter. This will apply back-off policy
+     * for the job; this policy can be adjusted through the
+     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method
+     * when the job is originally scheduled.  The job's initial
+     * requirements are preserved when jobs are rescheduled, regardless of backed-off
+     * policy.
+     * <p class="note">
+     * A job running while the device is dozing will not be rescheduled with the normal back-off
+     * policy.  Instead, the job will be re-added to the queue and executed again during
+     * a future idle maintenance window.
      * </p>
      *
-     * @param params Parameters specifying system-provided info about this job, this was given to
-     *               your application in {@link #onStartJob(JobParameters)}.
-     * @param needsReschedule True if this job should be rescheduled according to the back-off
-     *                        criteria specified at schedule-time. False otherwise.
+     * @param params The parameters identifying this job, as supplied to
+     *               the job in the {@link #onStartJob(JobParameters)} callback.
+     * @param wantsReschedule {@code true} if this job should be rescheduled according
+     *     to the back-off criteria specified when it was first scheduled; {@code false}
+     *     otherwise.
      */
-    public final void jobFinished(JobParameters params, boolean needsReschedule) {
-        mEngine.jobFinished(params, needsReschedule);
+    public final void jobFinished(JobParameters params, boolean wantsReschedule) {
+        mEngine.jobFinished(params, wantsReschedule);
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index be7f921..143c51d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -467,6 +467,7 @@
     /** Updates the flags for the given permission. */
     public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
             @NonNull String packageName, int flagMask, int flagValues, int userId);
-    /** temporary until mPermissionTrees is moved to PermissionManager */
-    public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
+    /** Returns a PermissionGroup. */
+    public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP(
+            @NonNull String groupName);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6c7c8a07..ad36139a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3711,17 +3711,15 @@
                 ai.flags |= ApplicationInfo.FLAG_IS_GAME;
             }
 
-            if (false) {
-                if (sa.getBoolean(
-                        com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
-                        false)) {
-                    ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+                    false)) {
+                ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
 
-                    // A heavy-weight application can not be in a custom process.
-                    // We can do direct compare because we intern all strings.
-                    if (ai.processName != null && ai.processName != ai.packageName) {
-                        outError[0] = "cantSaveState applications can not use custom processes";
-                    }
+                // A heavy-weight application can not be in a custom process.
+                // We can do direct compare because we intern all strings.
+                if (ai.processName != null && !ai.processName.equals(ai.packageName)) {
+                    outError[0] = "cantSaveState applications can not use custom processes";
                 }
             }
         }
@@ -6849,6 +6847,11 @@
             dest.writeParcelable(group, flags);
         }
 
+        /** @hide */
+        public boolean isAppOp() {
+            return info.isAppOp();
+        }
+
         private Permission(Parcel in) {
             super(in);
             final ClassLoader boot = Object.class.getClassLoader();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index b45c26c..5dd7aed 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -353,6 +353,11 @@
         return size;
     }
 
+    /** @hide */
+    public boolean isAppOp() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
     public static final Creator<PermissionInfo> CREATOR =
         new Creator<PermissionInfo>() {
         @Override
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 6b9c753..2f78161 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.app.TaskStackBuilder;
 import android.content.ComponentName;
@@ -100,6 +101,13 @@
     /** @hide When this is set, the bitmap icon is waiting to be saved. */
     public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
 
+    /**
+     * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
+     * installed yet.
+     * @hide
+     */
+    public static final int FLAG_SHADOW = 1 << 12;
+
     /** @hide */
     @IntDef(flag = true,
             value = {
@@ -158,6 +166,91 @@
     public @interface CloneFlags {}
 
     /**
+     * Shortcut is not disabled.
+     */
+    public static final int DISABLED_REASON_NOT_DISABLED = 0;
+
+    /**
+     * Shortcut has been disabled by the publisher app with the
+     * {@link ShortcutManager#disableShortcuts(List)} API.
+     */
+    public static final int DISABLED_REASON_BY_APP = 1;
+
+    /**
+     * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
+     * no longer exists.)
+     */
+    public static final int DISABLED_REASON_APP_CHANGED = 2;
+
+    /**
+     * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
+     * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
+     * ({@link #isVisibleToPublisher()} will be false.)
+     */
+    private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
+
+    /**
+     * Shortcut has been restored from the previous device, but the publisher app on the current
+     * device is of a lower version. The shortcut will not be usable until the app is upgraded to
+     * the same version or higher.
+     */
+    public static final int DISABLED_REASON_VERSION_LOWER = 100;
+
+    /**
+     * Shortcut has not been restored because the publisher app does not support backup and restore.
+     */
+    public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
+
+    /**
+     * Shortcut has not been restored because the publisher app's signature has changed.
+     */
+    public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
+
+    /**
+     * Shortcut has not been restored for unknown reason.
+     */
+    public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
+
+    /** @hide */
+    @IntDef(value = {
+            DISABLED_REASON_NOT_DISABLED,
+            DISABLED_REASON_BY_APP,
+            DISABLED_REASON_APP_CHANGED,
+            DISABLED_REASON_VERSION_LOWER,
+            DISABLED_REASON_BACKUP_NOT_SUPPORTED,
+            DISABLED_REASON_SIGNATURE_MISMATCH,
+            DISABLED_REASON_OTHER_RESTORE_ISSUE,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisabledReason{}
+
+    /** @hide */
+    public static String getDisabledReasonLabel(@DisabledReason int disabledReason) {
+        switch (disabledReason) {
+            case DISABLED_REASON_NOT_DISABLED:
+                return "[Not disabled]";
+            case DISABLED_REASON_BY_APP:
+                return "[Disabled: by app]";
+            case DISABLED_REASON_APP_CHANGED:
+                return "[Disabled: app changed]";
+            case DISABLED_REASON_VERSION_LOWER:
+                return "[Disabled: lower version]";
+            case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
+                return "[Disabled: backup not supported]";
+            case DISABLED_REASON_SIGNATURE_MISMATCH:
+                return "[Disabled: signature mismatch]";
+            case DISABLED_REASON_OTHER_RESTORE_ISSUE:
+                return "[Disabled: unknown restore issue]";
+        }
+        return "[Disabled: unknown reason:" + disabledReason + "]";
+    }
+
+    /** @hide */
+    public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
+        return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
+    }
+
+    /**
      * Shortcut category for messaging related actions, such as chat.
      */
     public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -240,6 +333,11 @@
 
     private final int mUserId;
 
+    /** @hide */
+    public static final int VERSION_CODE_UNKNOWN = -1;
+
+    private int mDisabledReason;
+
     private ShortcutInfo(Builder b) {
         mUserId = b.mContext.getUserId();
 
@@ -352,6 +450,7 @@
         mActivity = source.mActivity;
         mFlags = source.mFlags;
         mLastChangedTimestamp = source.mLastChangedTimestamp;
+        mDisabledReason = source.mDisabledReason;
 
         // Just always keep it since it's cheep.
         mIconResId = source.mIconResId;
@@ -615,13 +714,23 @@
 
     /**
      * @hide
+     *
+     * @isUpdating set true if it's "update", as opposed to "replace".
      */
-    public void ensureUpdatableWith(ShortcutInfo source) {
+    public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
+        if (isUpdating) {
+            Preconditions.checkState(isVisibleToPublisher(),
+                    "[Framework BUG] Invisible shortcuts can't be updated");
+        }
         Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
         Preconditions.checkState(mId.equals(source.mId), "ID must match");
         Preconditions.checkState(mPackageName.equals(source.mPackageName),
                 "Package name must match");
-        Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+
+        if (isVisibleToPublisher()) {
+            // Don't do this check for restore-blocked shortcuts.
+            Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+        }
     }
 
     /**
@@ -638,7 +747,7 @@
      * @hide
      */
     public void copyNonNullFieldsFrom(ShortcutInfo source) {
-        ensureUpdatableWith(source);
+        ensureUpdatableWith(source, /*isUpdating=*/ true);
 
         if (source.mActivity != null) {
             mActivity = source.mActivity;
@@ -1169,6 +1278,19 @@
         return mDisabledMessageResId;
     }
 
+    /** @hide */
+    public void setDisabledReason(@DisabledReason int reason) {
+        mDisabledReason = reason;
+    }
+
+    /**
+     * Returns why a shortcut has been disabled.
+     */
+    @DisabledReason
+    public int getDisabledReason() {
+        return mDisabledReason;
+    }
+
     /**
      * Return the shortcut's categories.
      *
@@ -1403,6 +1525,21 @@
         return hasFlags(FLAG_IMMUTABLE);
     }
 
+    /** @hide */
+    public boolean isDynamicVisible() {
+        return isDynamic() && isVisibleToPublisher();
+    }
+
+    /** @hide */
+    public boolean isPinnedVisible() {
+        return isPinned() && isVisibleToPublisher();
+    }
+
+    /** @hide */
+    public boolean isManifestVisible() {
+        return isDeclaredInManifest() && isVisibleToPublisher();
+    }
+
     /**
      * Return if a shortcut is immutable, in which case it cannot be modified with any of
      * {@link ShortcutManager} APIs.
@@ -1491,6 +1628,18 @@
     }
 
     /**
+     * When the system wasn't able to restore a shortcut, it'll still be registered to the system
+     * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
+     * to launchers though.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isVisibleToPublisher() {
+        return !isDisabledForRestoreIssue(mDisabledReason);
+    }
+
+    /**
      * Return whether a shortcut only contains "key" information only or not.  If true, only the
      * following fields are available.
      * <ul>
@@ -1668,6 +1817,7 @@
         mFlags = source.readInt();
         mIconResId = source.readInt();
         mLastChangedTimestamp = source.readLong();
+        mDisabledReason = source.readInt();
 
         if (source.readInt() == 0) {
             return; // key information only.
@@ -1711,6 +1861,7 @@
         dest.writeInt(mFlags);
         dest.writeInt(mIconResId);
         dest.writeLong(mLastChangedTimestamp);
+        dest.writeInt(mDisabledReason);
 
         if (hasKeyFieldsOnly()) {
             dest.writeInt(0);
@@ -1808,6 +1959,11 @@
         sb.append(", flags=0x");
         sb.append(Integer.toHexString(mFlags));
         sb.append(" [");
+        if ((mFlags & FLAG_SHADOW) != 0) {
+            // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
+            // we don't have an isXxx for this.
+            sb.append("Sdw");
+        }
         if (!isEnabled()) {
             sb.append("Dis");
         }
@@ -1848,7 +2004,9 @@
         sb.append("packageName=");
         sb.append(mPackageName);
 
-        sb.append(", activity=");
+        addIndentOrComma(sb, indent);
+
+        sb.append("activity=");
         sb.append(mActivity);
 
         addIndentOrComma(sb, indent);
@@ -1883,6 +2041,11 @@
 
         addIndentOrComma(sb, indent);
 
+        sb.append("disabledReason=");
+        sb.append(getDisabledReasonLabel(mDisabledReason));
+
+        addIndentOrComma(sb, indent);
+
         sb.append("categories=");
         sb.append(mCategories);
 
@@ -1953,7 +2116,7 @@
             CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
             Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
             long lastChangedTimestamp,
-            int flags, int iconResId, String iconResName, String bitmapPath) {
+            int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) {
         mUserId = userId;
         mId = id;
         mPackageName = packageName;
@@ -1978,5 +2141,6 @@
         mIconResId = iconResId;
         mIconResName = iconResName;
         mBitmapPath = bitmapPath;
+        mDisabledReason = disabledReason;
     }
 }
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index a8b8c4b..386239c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -796,7 +796,7 @@
                 dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
                 is.close();
             }
-        } catch (Exception e) {
+        } catch (Exception | StackOverflowError e) {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
             final NotFoundException rnf = new NotFoundException(
                     "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9881927..450ced4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1911,6 +1911,13 @@
             long elapsedRealtimeUs, int which);
 
     /**
+     * Returns the {@link Timer} object that tracks the given screen brightness.
+     *
+     * {@hide}
+     */
+    public abstract Timer getScreenBrightnessTimer(int brightnessBin);
+
+    /**
      * Returns the time in microseconds that power save mode has been enabled while the device was
      * running on battery.
      *
@@ -2019,6 +2026,14 @@
             long elapsedRealtimeUs, int which);
 
     /**
+     * Returns the {@link Timer} object that tracks how much the phone has been trying to
+     * acquire a signal.
+     *
+     * {@hide}
+     */
+    public abstract Timer getPhoneSignalScanningTimer();
+
+    /**
      * Returns the number of times the phone has entered the given signal strength.
      *
      * {@hide}
@@ -2026,6 +2041,12 @@
     public abstract int getPhoneSignalStrengthCount(int strengthBin, int which);
 
     /**
+     * Return the {@link Timer} object used to track the given signal strength's duration and
+     * counts.
+     */
+    protected abstract Timer getPhoneSignalStrengthTimer(int strengthBin);
+
+    /**
      * Returns the time in microseconds that the mobile network has been active
      * (in a high power state).
      *
@@ -2108,6 +2129,11 @@
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
 
+    /**
+     * Returns the {@link Timer} object that tracks the phone's data connection type stats.
+     */
+    public abstract Timer getPhoneDataConnectionTimer(int dataType);
+
     public static final int WIFI_SUPPL_STATE_INVALID = 0;
     public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1;
     public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2;
@@ -2267,6 +2293,13 @@
     public abstract int getWifiStateCount(int wifiState, int which);
 
     /**
+     * Returns the {@link Timer} object that tracks the given WiFi state.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiStateTimer(int wifiState);
+
+    /**
      * Returns the time in microseconds that the wifi supplicant has been
      * in a given state.
      *
@@ -2282,6 +2315,13 @@
      */
     public abstract int getWifiSupplStateCount(int state, int which);
 
+    /**
+     * Returns the {@link Timer} object that tracks the given wifi supplicant state.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiSupplStateTimer(int state);
+
     public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5;
 
     /**
@@ -2301,6 +2341,13 @@
     public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
 
     /**
+     * Returns the {@link Timer} object that tracks the given WIFI signal strength.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiSignalStrengthTimer(int strengthBin);
+
+    /**
      * Returns the time in microseconds that the flashlight has been on while the device was
      * running on battery.
      *
@@ -2487,13 +2534,13 @@
     public abstract int getDischargeAmountScreenOffSinceCharge();
 
     /**
-     * Get the amount the battery has discharged while the screen was doze,
+     * Get the amount the battery has discharged while the screen was dozing,
      * since the last time power was unplugged.
      */
     public abstract int getDischargeAmountScreenDoze();
 
     /**
-     * Get the amount the battery has discharged while the screen was doze,
+     * Get the amount the battery has discharged while the screen was dozing,
      * since the last time the device was charged.
      */
     public abstract int getDischargeAmountScreenDozeSinceCharge();
@@ -2626,20 +2673,20 @@
      * micro-Ampere-hours. This will be non-zero only if the device's battery has
      * a coulomb counter.
      */
-    public abstract long getMahDischargeScreenOff(int which);
+    public abstract long getUahDischargeScreenOff(int which);
 
     /**
      * Return the amount of battery discharge while the screen was in doze mode, measured in
      * micro-Ampere-hours. This will be non-zero only if the device's battery has
      * a coulomb counter.
      */
-    public abstract long getMahDischargeScreenDoze(int which);
+    public abstract long getUahDischargeScreenDoze(int which);
 
     /**
      * Return the amount of battery discharge  measured in micro-Ampere-hours. This will be
      * non-zero only if the device's battery has a coulomb counter.
      */
-    public abstract long getMahDischarge(int which);
+    public abstract long getUahDischarge(int which);
 
     /**
      * Returns the estimated real battery capacity, which may be less than the capacity
@@ -2984,7 +3031,7 @@
             final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
                     / 1000;
             final int count = timer.getCountLocked(which);
-            if (totalTime != 0) {
+            if (totalTime != 0 || count != 0) {
                 dumpLine(pw, uid, category, type, totalTime, count);
             }
         }
@@ -3000,12 +3047,12 @@
      * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
      */
     private static void dumpTimer(ProtoOutputStream proto, long fieldId,
-                                        Timer timer, long rawRealtime, int which) {
+                                        Timer timer, long rawRealtimeUs, int which) {
         if (timer == null) {
             return;
         }
         // Convert from microseconds to milliseconds with rounding
-        final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+        final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
         final int count = timer.getCountLocked(which);
         if (totalTimeMs != 0 || count != 0) {
             final long token = proto.start(fieldId);
@@ -3191,13 +3238,13 @@
     /**
      * Checkin server version of dump to produce more compact, computer-readable log.
      *
-     * NOTE: all times are expressed in 'ms'.
+     * NOTE: all times are expressed in microseconds, unless specified otherwise.
      */
     public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
             boolean wifiOnly) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
-        final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
-        final long rawRealtimeMs = (rawRealtime + 500) / 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtime = rawRealtimeMs * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
         final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
         final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
@@ -3220,9 +3267,9 @@
                 rawRealtime, which);
         final int connChanges = getNumConnectivityChange(which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
-        final long dischargeCount = getMahDischarge(which);
-        final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
-        final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
+        final long dischargeCount = getUahDischarge(which);
+        final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
+        final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
 
         final StringBuilder sb = new StringBuilder(128);
 
@@ -3460,9 +3507,9 @@
                     BatteryStatsHelper.makemAh(helper.getComputedPower()),
                     BatteryStatsHelper.makemAh(helper.getMinDrainedPower()),
                     BatteryStatsHelper.makemAh(helper.getMaxDrainedPower()));
+            int uid = 0;
             for (int i=0; i<sippers.size(); i++) {
                 final BatterySipper bs = sippers.get(i);
-                int uid = 0;
                 String label;
                 switch (bs.drainType) {
                     case IDLE:
@@ -3503,6 +3550,9 @@
                     case CAMERA:
                         label = "camera";
                         break;
+                    case MEMORY:
+                        label = "memory";
+                        break;
                     default:
                         label = "???";
                 }
@@ -3523,6 +3573,7 @@
             dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
         }
 
+        // Dump stats per UID.
         for (int iu = 0; iu < NU; iu++) {
             final int uid = uidStats.keyAt(iu);
             if (reqUid >= 0 && uid != reqUid) {
@@ -4020,7 +4071,7 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeCount = getMahDischarge(which);
+        final long dischargeCount = getUahDischarge(which);
         if (dischargeCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -4030,7 +4081,7 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
+        final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
         if (dischargeScreenOffCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -4040,7 +4091,7 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
+        final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
         if (dischargeScreenDozeCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -6038,6 +6089,61 @@
         return true;
     }
 
+    private static void dumpDurationSteps(ProtoOutputStream proto, long fieldId,
+            LevelStepTracker steps) {
+        if (steps == null) {
+            return;
+        }
+        int count = steps.mNumStepDurations;
+        long token;
+        for (int i = 0; i < count; ++i) {
+            token = proto.start(fieldId);
+            proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
+            proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
+
+            final long initMode = steps.getInitModeAt(i);
+            final long modMode = steps.getModModeAt(i);
+
+            int ds = SystemProto.BatteryLevelStep.DS_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+                switch ((int) (initMode & STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                    case Display.STATE_OFF:
+                        ds = SystemProto.BatteryLevelStep.DS_OFF;
+                        break;
+                    case Display.STATE_ON:
+                        ds = SystemProto.BatteryLevelStep.DS_ON;
+                        break;
+                    case Display.STATE_DOZE:
+                        ds = SystemProto.BatteryLevelStep.DS_DOZE;
+                        break;
+                    case Display.STATE_DOZE_SUSPEND:
+                        ds = SystemProto.BatteryLevelStep.DS_DOZE_SUSPEND;
+                        break;
+                    default:
+                        ds = SystemProto.BatteryLevelStep.DS_ERROR;
+                        break;
+                }
+            }
+            proto.write(SystemProto.BatteryLevelStep.DISPLAY_STATE, ds);
+
+            int psm = SystemProto.BatteryLevelStep.PSM_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+                psm = (initMode & STEP_LEVEL_MODE_POWER_SAVE) != 0
+                    ? SystemProto.BatteryLevelStep.PSM_ON : SystemProto.BatteryLevelStep.PSM_OFF;
+            }
+            proto.write(SystemProto.BatteryLevelStep.POWER_SAVE_MODE, psm);
+
+            int im = SystemProto.BatteryLevelStep.IM_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                im = (initMode & STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+                    ? SystemProto.BatteryLevelStep.IM_ON : SystemProto.BatteryLevelStep.IM_OFF;
+            }
+            proto.write(SystemProto.BatteryLevelStep.IDLE_MODE, im);
+
+            proto.end(token);
+        }
+    }
+
     public static final int DUMP_CHARGED_ONLY = 1<<1;
     public static final int DUMP_DAILY_ONLY = 1<<2;
     public static final int DUMP_HISTORY_ONLY = 1<<3;
@@ -6463,7 +6569,7 @@
         }
     }
 
-    /** Dump batterystats data to a proto. @hide */
+    /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */
     public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
             int flags, long historyStart) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -6485,10 +6591,376 @@
 
         if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
             // TODO: implement dumpProtoAppsLocked(proto, apps);
-            // TODO: implement dumpProtoSystemLocked(proto);
+            dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
         }
 
         proto.end(bToken);
         proto.flush();
     }
+
+    private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
+        final long sToken = proto.start(BatteryStatsProto.SYSTEM);
+        final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtimeUs = rawRealtimeMs * 1000;
+        final int which = STATS_SINCE_CHARGED;
+
+        // Battery data (BATTERY_DATA)
+        long token = proto.start(SystemProto.BATTERY);
+        proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
+        proto.write(SystemProto.Battery.START_COUNT, getStartCount());
+        proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
+                computeRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.TOTAL_UPTIME_MS,
+                computeUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.BATTERY_REALTIME_MS,
+                computeBatteryRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.BATTERY_UPTIME_MS,
+                computeBatteryUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_OFF_REALTIME_MS,
+                computeBatteryScreenOffRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_OFF_UPTIME_MS,
+                computeBatteryScreenOffUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_DOZE_DURATION_MS,
+                getScreenDozeTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.ESTIMATED_BATTERY_CAPACITY_MAH,
+                getEstimatedBatteryCapacity());
+        proto.write(SystemProto.Battery.MIN_LEARNED_BATTERY_CAPACITY_UAH,
+                getMinLearnedBatteryCapacity());
+        proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
+                getMaxLearnedBatteryCapacity());
+        proto.end(token);
+
+        // Battery discharge (BATTERY_DISCHARGE_DATA)
+        token = proto.start(SystemProto.BATTERY_DISCHARGE);
+        proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
+                getLowDischargeAmountSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
+                getHighDischargeAmountSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_ON_SINCE_CHARGE,
+                getDischargeAmountScreenOnSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_OFF_SINCE_CHARGE,
+                getDischargeAmountScreenOffSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_DOZE_SINCE_CHARGE,
+                getDischargeAmountScreenDozeSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH,
+                getUahDischarge(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_OFF,
+                getUahDischargeScreenOff(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
+                getUahDischargeScreenDoze(which) / 1000);
+        proto.end(token);
+
+        // Time remaining
+        long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+        if (timeRemainingUs >= 0) {
+            // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
+            proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+        } else {
+            timeRemainingUs = computeBatteryTimeRemaining(rawRealtimeUs);
+            // Discharge time remaining (DISCHARGE_TIME_REMAIN_DATA)
+            if (timeRemainingUs >= 0) {
+                proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+            } else {
+                proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, -1);
+            }
+        }
+
+        // Charge step (CHARGE_STEP_DATA)
+        dumpDurationSteps(proto, SystemProto.CHARGE_STEP, getChargeLevelStepTracker());
+
+        // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
+        for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
+            token = proto.start(SystemProto.DATA_CONNECTION);
+            proto.write(SystemProto.DataConnection.NAME, i);
+            dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Discharge step (DISCHARGE_STEP_DATA)
+        dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());
+
+        // CPU frequencies (GLOBAL_CPU_FREQ_DATA)
+        final long[] cpuFreqs = getCpuFreqs();
+        if (cpuFreqs != null) {
+            for (long i : cpuFreqs) {
+                proto.write(SystemProto.CPU_FREQUENCY, i);
+            }
+        }
+
+        // Bluetooth controller (GLOBAL_BLUETOOTH_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_BLUETOOTH_CONTROLLER,
+                getBluetoothControllerActivity(), which);
+
+        // Modem controller (GLOBAL_MODEM_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_MODEM_CONTROLLER,
+                getModemControllerActivity(), which);
+
+        // Global network data (GLOBAL_NETWORK_DATA)
+        token = proto.start(SystemProto.GLOBAL_NETWORK);
+        proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_RX,
+                getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_TX,
+                getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_RX,
+                getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_TX,
+                getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.BT_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+        proto.end(token);
+
+        // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
+                getWifiControllerActivity(), which);
+
+
+        // Global wifi (GLOBAL_WIFI_DATA)
+        token = proto.start(SystemProto.GLOBAL_WIFI);
+        proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
+                getWifiOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
+                getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
+        proto.end(token);
+
+        // Kernel wakelock (KERNEL_WAKELOCK_DATA)
+        final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+        for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
+            token = proto.start(SystemProto.KERNEL_WAKELOCK);
+            proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Misc (MISC_DATA)
+        // Calculate wakelock times across all uids.
+        long fullWakeLockTimeTotalUs = 0;
+        long partialWakeLockTimeTotalUs = 0;
+
+        final SparseArray<? extends Uid> uidStats = getUidStats();
+        for (int iu = 0; iu < uidStats.size(); iu++) {
+            final Uid u = uidStats.valueAt(iu);
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks =
+                    u.getWakelockStats();
+            for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotalUs += fullWakeTimer.getTotalTimeLocked(rawRealtimeUs,
+                            which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    partialWakeLockTimeTotalUs += partialWakeTimer.getTotalTimeLocked(
+                        rawRealtimeUs, which);
+                }
+            }
+        }
+        token = proto.start(SystemProto.MISC);
+        proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
+                getScreenOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
+                getPhoneOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.FULL_WAKELOCK_TOTAL_DURATION_MS,
+                fullWakeLockTimeTotalUs / 1000);
+        proto.write(SystemProto.Misc.PARTIAL_WAKELOCK_TOTAL_DURATION_MS,
+                partialWakeLockTimeTotalUs / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_DURATION_MS,
+                getMobileRadioActiveTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_ADJUSTED_TIME_MS,
+                getMobileRadioActiveAdjustedTime(which) / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_COUNT,
+                getMobileRadioActiveCount(which));
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_UNKNOWN_DURATION_MS,
+                getMobileRadioActiveUnknownTime(which) / 1000);
+        proto.write(SystemProto.Misc.INTERACTIVE_DURATION_MS,
+                getInteractiveTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.BATTERY_SAVER_MODE_ENABLED_DURATION_MS,
+                getPowerSaveModeEnabledTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.NUM_CONNECTIVITY_CHANGES,
+                getNumConnectivityChange(which));
+        proto.write(SystemProto.Misc.DEEP_DOZE_ENABLED_DURATION_MS,
+                getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.DEEP_DOZE_COUNT,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which));
+        proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_DURATION_MS,
+                getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_COUNT,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which));
+        proto.write(SystemProto.Misc.LONGEST_DEEP_DOZE_DURATION_MS,
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
+        proto.write(SystemProto.Misc.LIGHT_DOZE_ENABLED_DURATION_MS,
+                getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.LIGHT_DOZE_COUNT,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
+        proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_DURATION_MS,
+                getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_COUNT,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
+        proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
+        proto.end(token);
+
+        final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+        helper.create(this);
+        helper.refreshStats(which, UserHandle.USER_ALL);
+
+        // Power use item (POWER_USE_ITEM_DATA)
+        final List<BatterySipper> sippers = helper.getUsageList();
+        if (sippers != null) {
+            for (int i = 0; i < sippers.size(); ++i) {
+                final BatterySipper bs = sippers.get(i);
+                int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
+                int uid = 0;
+                switch (bs.drainType) {
+                    case IDLE:
+                        n = SystemProto.PowerUseItem.IDLE;
+                        break;
+                    case CELL:
+                        n = SystemProto.PowerUseItem.CELL;
+                        break;
+                    case PHONE:
+                        n = SystemProto.PowerUseItem.PHONE;
+                        break;
+                    case WIFI:
+                        n = SystemProto.PowerUseItem.WIFI;
+                        break;
+                    case BLUETOOTH:
+                        n = SystemProto.PowerUseItem.BLUETOOTH;
+                        break;
+                    case SCREEN:
+                        n = SystemProto.PowerUseItem.SCREEN;
+                        break;
+                    case FLASHLIGHT:
+                        n = SystemProto.PowerUseItem.FLASHLIGHT;
+                        break;
+                    case APP:
+                        // dumpProtoAppLocked will handle this.
+                        continue;
+                    case USER:
+                        n = SystemProto.PowerUseItem.USER;
+                        uid = UserHandle.getUid(bs.userId, 0);
+                        break;
+                    case UNACCOUNTED:
+                        n = SystemProto.PowerUseItem.UNACCOUNTED;
+                        break;
+                    case OVERCOUNTED:
+                        n = SystemProto.PowerUseItem.OVERCOUNTED;
+                        break;
+                    case CAMERA:
+                        n = SystemProto.PowerUseItem.CAMERA;
+                        break;
+                    case MEMORY:
+                        n = SystemProto.PowerUseItem.MEMORY;
+                        break;
+                }
+                token = proto.start(SystemProto.POWER_USE_ITEM);
+                proto.write(SystemProto.PowerUseItem.NAME, n);
+                proto.write(SystemProto.PowerUseItem.UID, uid);
+                proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
+                proto.write(SystemProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
+                proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
+                proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+                        bs.proportionalSmearMah);
+                proto.end(token);
+            }
+        }
+
+        // Power use summary (POWER_USE_SUMMARY_DATA)
+        token = proto.start(SystemProto.POWER_USE_SUMMARY);
+        proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
+                helper.getPowerProfile().getBatteryCapacity());
+        proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
+        proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
+        proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
+        proto.end(token);
+
+        // RPM stats (RESOURCE_POWER_MANAGER_DATA)
+        final Map<String, ? extends Timer> rpmStats = getRpmStats();
+        final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+        for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+            token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+            proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
+                    ent.getValue(), rawRealtimeUs, which);
+            dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
+                    screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Screen brightness (SCREEN_BRIGHTNESS_DATA)
+        for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
+            token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+            proto.write(SystemProto.ScreenBrightness.NAME, i);
+            dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
+        dumpTimer(proto, SystemProto.SIGNAL_SCANNING, getPhoneSignalScanningTimer(), rawRealtimeUs,
+                which);
+
+        // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
+        for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
+            token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+            proto.write(SystemProto.PhoneSignalStrength.NAME, i);
+            dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Wakeup reasons (WAKEUP_REASON_DATA)
+        final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+        for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+            token = proto.start(SystemProto.WAKEUP_REASON);
+            proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
+            token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+            proto.write(SystemProto.WifiSignalStrength.NAME, i);
+            dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_STATES; ++i) {
+            token = proto.start(SystemProto.WIFI_STATE);
+            proto.write(SystemProto.WifiState.NAME, i);
+            dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
+            token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+            proto.write(SystemProto.WifiSupplicantState.NAME, i);
+            dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(token);
+        }
+
+        proto.end(sToken);
+    }
 }
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index f8f2813..daacc4e 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -45,4 +45,20 @@
      * Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
      */
     void informPollAlarmFired();
+
+    /**
+     * Inform statsd what the version and package are for each uid. Note that each array should
+     * have the same number of elements, and version[i] and package[i] correspond to uid[i].
+     */
+    oneway void informAllUidData(in int[] uid, in int[] version, in String[] app);
+
+    /**
+     * Inform statsd what the uid and version are for one app that was updated.
+     */
+    oneway void informOnePackage(in String app, in int uid, in int version);
+
+    /**
+     * Inform stats that an app was removed.
+     */
+    oneway void informOnePackageRemoved(in String app, in int uid);
 }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c091420..7f588ad 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -737,7 +737,9 @@
     private void closeWithStatus(int status, String msg) {
         if (mClosed) return;
         mClosed = true;
-        mGuard.close();
+        if (mGuard != null) {
+            mGuard.close();
+        }
         // Status MUST be sent before closing actual descriptor
         writeCommStatusAndClose(status, msg);
         IoUtils.closeQuietly(mFd);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 430a5e3..8c68871 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -574,6 +574,25 @@
     public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
 
     /**
+     * Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
+     * In this case, the system will force-stop the app as if the user chooses the "close app"
+     * option on the UI. No feedback report will be collected as there is no way for the user to
+     * provide explicit consent.
+     *
+     * When this user restriction is set by device owners, it's applied to all users; when it's set
+     * by profile owners, it's only applied to the relevant profiles.
+     * The default value is <code>false</code>.
+     *
+     * <p>This user restriction has no effect on managed profiles.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+
+    /**
      * Specifies if what is copied in the clipboard of this profile can
      * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
      * pasted in this profile.
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index ee8eed1..3d2e1d1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -206,8 +206,7 @@
                     try {
                         mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
                                 .getAudioAttributes())
-                                .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
-                                        AudioAttributes.FLAG_BYPASS_MUTE)
+                                .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
                                 .build());
                         mRingtone.play();
                     } catch (Throwable e) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a062db4..584dad0 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6792,14 +6792,6 @@
                 "lock_screen_show_notifications";
 
         /**
-         * This preference stores the last stack active task time for each user, which affects what
-         * tasks will be visible in Overview.
-         * @hide
-         */
-        public static final String OVERVIEW_LAST_STACK_ACTIVE_TIME =
-                "overview_last_stack_active_time";
-
-        /**
          * List of TV inputs that are currently hidden. This is a string
          * containing the IDs of all hidden TV inputs. Each ID is encoded by
          * {@link android.net.Uri#encode(String)} and separated by ':'.
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 2e59f6c..9a25f5b 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -365,6 +365,81 @@
  * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
  * verifications above, as long as the service can verify the authenticity of the browser app by
  * checking its signing certificate.
+ *
+ * <a name="MultipleStepsSave"></a>
+ * <h3>Saving when data is split in multiple screens</h3>
+ *
+ * Apps often split the user data in multiple screens in the same activity, specially in
+ * activities used to create a new user account. For example, the first screen asks for a username,
+ * and if the username is available, it moves to a second screen, which asks for a password.
+ *
+ * <p>It's tricky to handle save for autofill in these situations, because the autofill service must
+ * wait until the user enters both fields before the autofill save UI can be shown. But it can be
+ * done by following the steps below:
+ *
+ * <ol>
+ * <li>In the first
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
+ * adds a {@link FillResponse.Builder#setClientState(android.os.Bundle) client state bundle} in
+ * the response, containing the autofill ids of the partial fields present in the screen.
+ * <li>In the second
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
+ * retrieves the {@link FillRequest#getClientState() client state bundle}, gets the autofill ids
+ * set in the previous request from the client state, and adds these ids and the
+ * {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} to the {@link SaveInfo} used in the second
+ * response.
+ * <li>In the {@link #onSaveRequest(SaveRequest, SaveCallback) save request}, the service uses the
+ * proper {@link FillContext fill contexts} to get the value of each field (there is one fill
+ * context per fill request).
+ * </ol>
+ *
+ * <p>For example, in an app that uses 2 steps for the username and password fields, the workflow
+ * would be:
+ * <pre class="prettyprint">
+ *  // On first fill request
+ *  AutofillId usernameId = // parse from AssistStructure;
+ *  Bundle clientState = new Bundle();
+ *  clientState.putParcelable("usernameId", usernameId);
+ *  fillCallback.onSuccess(
+ *    new FillResponse.Builder()
+ *        .setClientState(clientState)
+ *        .setSaveInfo(new SaveInfo
+ *             .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME, new AutofillId[] {usernameId})
+ *             .build())
+ *        .build());
+ *
+ *  // On second fill request
+ *  Bundle clientState = fillRequest.getClientState();
+ *  AutofillId usernameId = clientState.getParcelable("usernameId");
+ *  AutofillId passwordId = // parse from AssistStructure
+ *  clientState.putParcelable("passwordId", passwordId);
+ *  fillCallback.onSuccess(
+ *    new FillResponse.Builder()
+ *        .setClientState(clientState)
+ *        .setSaveInfo(new SaveInfo
+ *             .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
+ *                      new AutofillId[] {usernameId, passwordId})
+ *             .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+ *             .build())
+ *        .build());
+ *
+ *  // On save request
+ *  Bundle clientState = saveRequest.getClientState();
+ *  AutofillId usernameId = clientState.getParcelable("usernameId");
+ *  AutofillId passwordId = clientState.getParcelable("passwordId");
+ *  List<FillContext> fillContexts = saveRequest.getFillContexts();
+ *
+ *  FillContext usernameContext = fillContexts.get(0);
+ *  ViewNode usernameNode = findNodeByAutofillId(usernameContext.getStructure(), usernameId);
+ *  AutofillValue username = usernameNode.getAutofillValue().getTextValue().toString();
+ *
+ *  FillContext passwordContext = fillContexts.get(1);
+ *  ViewNode passwordNode = findNodeByAutofillId(passwordContext.getStructure(), passwordId);
+ *  AutofillValue password = passwordNode.getAutofillValue().getTextValue().toString();
+ *
+ *  save(username, password);
+ *
+ * </pre>
  */
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 65fdb5c..f53967b 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
-import android.os.CancellationSignal;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -60,9 +59,14 @@
     }
 
     /**
-     * Gets the extra client state returned from the last {@link
-     * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}
-     * fill request}.
+     * Gets the latest client state extra returned from the service.
+     *
+     * <p><b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state
+     * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} where considered. On
+     * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of
+     * an authenticated request through the
+     * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are
+     * also considered (and take precedence when set).
      *
      * @return The client state.
      */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 7bec898..c5615ae 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -76,10 +76,13 @@
     private static final int DAY_MINUTES = 24 * 60;
     private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
 
-    private static final boolean DEFAULT_ALLOW_CALLS = true;
+    // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
+    private static final boolean DEFAULT_ALLOW_ALARMS = true;
+    private static final boolean DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER = true;
+    private static final boolean DEFAULT_ALLOW_CALLS = false;
     private static final boolean DEFAULT_ALLOW_MESSAGES = false;
-    private static final boolean DEFAULT_ALLOW_REMINDERS = true;
-    private static final boolean DEFAULT_ALLOW_EVENTS = true;
+    private static final boolean DEFAULT_ALLOW_REMINDERS = false;
+    private static final boolean DEFAULT_ALLOW_EVENTS = false;
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
     private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
     private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
@@ -89,6 +92,8 @@
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
     private static final String ALLOW_TAG = "allow";
+    private static final String ALLOW_ATT_ALARMS = "alarms";
+    private static final String ALLOW_ATT_MEDIA = "media_system_other";
     private static final String ALLOW_ATT_CALLS = "calls";
     private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
     private static final String ALLOW_ATT_MESSAGES = "messages";
@@ -100,8 +105,6 @@
     private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
     private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
 
-    private static final String CONDITION_TAG = "condition";
-    private static final String CONDITION_ATT_COMPONENT = "component";
     private static final String CONDITION_ATT_ID = "id";
     private static final String CONDITION_ATT_SUMMARY = "summary";
     private static final String CONDITION_ATT_LINE1 = "line1";
@@ -123,6 +126,8 @@
     private static final String RULE_ATT_CREATION_TIME = "creationTime";
     private static final String RULE_ATT_ENABLER = "enabler";
 
+    public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
+    public boolean allowMediaSystemOther = DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER;
     public boolean allowCalls = DEFAULT_ALLOW_CALLS;
     public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
     public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
@@ -161,6 +166,8 @@
         }
         allowWhenScreenOff = source.readInt() == 1;
         allowWhenScreenOn = source.readInt() == 1;
+        allowAlarms = source.readInt() == 1;
+        allowMediaSystemOther = source.readInt() == 1;
     }
 
     @Override
@@ -190,19 +197,23 @@
         }
         dest.writeInt(allowWhenScreenOff ? 1 : 0);
         dest.writeInt(allowWhenScreenOn ? 1 : 0);
+        dest.writeInt(allowAlarms ? 1 : 0);
+        dest.writeInt(allowMediaSystemOther ? 1 : 0);
     }
 
     @Override
     public String toString() {
         return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
                 .append("user=").append(user)
+                .append(",allowAlarms=").append(allowAlarms)
+                .append(",allowMediaSystemOther=").append(allowMediaSystemOther)
+                .append(",allowReminders=").append(allowReminders)
+                .append(",allowEvents=").append(allowEvents)
                 .append(",allowCalls=").append(allowCalls)
                 .append(",allowRepeatCallers=").append(allowRepeatCallers)
                 .append(",allowMessages=").append(allowMessages)
                 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
                 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
-                .append(",allowReminders=").append(allowReminders)
-                .append(",allowEvents=").append(allowEvents)
                 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
                 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
                 .append(",automaticRules=").append(automaticRules)
@@ -218,9 +229,21 @@
         if (user != to.user) {
             d.addLine("user", user, to.user);
         }
+        if (allowAlarms != to.allowAlarms) {
+            d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
+        }
+        if (allowMediaSystemOther != to.allowMediaSystemOther) {
+            d.addLine("allowMediaSystemOther", allowMediaSystemOther, to.allowMediaSystemOther);
+        }
         if (allowCalls != to.allowCalls) {
             d.addLine("allowCalls", allowCalls, to.allowCalls);
         }
+        if (allowReminders != to.allowReminders) {
+            d.addLine("allowReminders", allowReminders, to.allowReminders);
+        }
+        if (allowEvents != to.allowEvents) {
+            d.addLine("allowEvents", allowEvents, to.allowEvents);
+        }
         if (allowRepeatCallers != to.allowRepeatCallers) {
             d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
         }
@@ -233,12 +256,6 @@
         if (allowMessagesFrom != to.allowMessagesFrom) {
             d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
         }
-        if (allowReminders != to.allowReminders) {
-            d.addLine("allowReminders", allowReminders, to.allowReminders);
-        }
-        if (allowEvents != to.allowEvents) {
-            d.addLine("allowEvents", allowEvents, to.allowEvents);
-        }
         if (allowWhenScreenOff != to.allowWhenScreenOff) {
             d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
         }
@@ -335,7 +352,9 @@
         if (!(o instanceof ZenModeConfig)) return false;
         if (o == this) return true;
         final ZenModeConfig other = (ZenModeConfig) o;
-        return other.allowCalls == allowCalls
+        return other.allowAlarms == allowAlarms
+                && other.allowMediaSystemOther == allowMediaSystemOther
+                && other.allowCalls == allowCalls
                 && other.allowRepeatCallers == allowRepeatCallers
                 && other.allowMessages == allowMessages
                 && other.allowCallsFrom == allowCallsFrom
@@ -351,10 +370,10 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
-                allowMessagesFrom, allowReminders, allowEvents, allowWhenScreenOff,
-                allowWhenScreenOn,
-                user, automaticRules, manualRule);
+        return Objects.hash(allowAlarms, allowMediaSystemOther, allowCalls,
+                allowRepeatCallers, allowMessages,
+                allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
+                allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule);
     }
 
     private static String toDayList(int[] days) {
@@ -413,10 +432,12 @@
             }
             if (type == XmlPullParser.START_TAG) {
                 if (ALLOW_TAG.equals(tag)) {
-                    rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
+                    rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
+                            DEFAULT_ALLOW_CALLS);
                     rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
                             DEFAULT_ALLOW_REPEAT_CALLERS);
-                    rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
+                    rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
+                            DEFAULT_ALLOW_MESSAGES);
                     rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
                             DEFAULT_ALLOW_REMINDERS);
                     rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
@@ -438,6 +459,9 @@
                             safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
                     rt.allowWhenScreenOn =
                             safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
+                    rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
+                    rt.allowMediaSystemOther = safeBoolean(parser, ALLOW_ATT_MEDIA,
+                            DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER);
                 } else if (MANUAL_TAG.equals(tag)) {
                     rt.manualRule = readRuleXml(parser);
                 } else if (AUTOMATIC_TAG.equals(tag)) {
@@ -468,6 +492,8 @@
         out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
         out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
         out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
+        out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
+        out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowMediaSystemOther));
         out.endTag(null, ALLOW_TAG);
 
         if (manualRule != null) {
@@ -654,6 +680,12 @@
         if (!allowWhenScreenOn) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
         }
+        if (allowAlarms) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+        }
+        if (allowMediaSystemOther) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER;
+        }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
@@ -680,10 +712,13 @@
 
     public void applyNotificationPolicy(Policy policy) {
         if (policy == null) return;
-        allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
-        allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+        allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
+        allowMediaSystemOther = (policy.priorityCategories
+                & Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0;
         allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
         allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
+        allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
+        allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
         allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
                 != 0;
         allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 24260c4..fba358c 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -299,7 +299,7 @@
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
-        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
+        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
     }
 
     /**
@@ -440,7 +440,7 @@
             mEllipsizeAt = null;
         }
 
-        mObjects = new PackedObjectVector<Directions>(1);
+        mObjects = new PackedObjectVector<>(1);
 
         // Initial state is a single line with 0 characters (0 to 0), with top at 0 and bottom at
         // whatever is natural, and undefined ellipsis.
@@ -1050,7 +1050,7 @@
 
     private static class ChangeWatcher implements TextWatcher, SpanWatcher {
         public ChangeWatcher(DynamicLayout layout) {
-            mLayout = new WeakReference<DynamicLayout>(layout);
+            mLayout = new WeakReference<>(layout);
         }
 
         private void reflow(CharSequence s, int where, int before, int after) {
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index ad26f23..ddfc00c 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -129,7 +129,6 @@
             final String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
             final File patternFile = new File(SYSTEM_HYPHENATOR_LOCATION, patternFilename);
             if (!patternFile.canRead()) {
-                Log.e(TAG, "hyphenation patterns for " + patternFile + " not found or unreadable");
                 mDataAddress = 0;
             } else {
                 long address;
@@ -230,6 +229,11 @@
             loadData("tk", 2, 2); // Turkmen
             loadData("und-Ethi", 1, 1); // Any language in Ethiopic script
 
+            // Following two hyphenators do not have pattern files but there is some special logic
+            // based on language.
+            loadData("ca", 2, 2);  // Catalan
+            loadData("pl", 2, 2);  // Polish
+
             // English locales that fall back to en-US. The data is
             // from CLDR. It's all English locales, minus the locales whose
             // parent is en-001 (from supplementalData.xml, under <parentLocales>).
@@ -267,7 +271,7 @@
         }
     };
 
-    private static native long nBuildHyphenator(/* non-zero */ long dataAddress,
+    private static native long nBuildHyphenator(long dataAddress,
             @NonNull String langTag, @IntRange(from = 1) int minPrefix,
             @IntRange(from = 1) int minSuffix);
 }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 60fff73..ac5c2e9 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -319,8 +319,6 @@
 
     private float getJustifyWidth(int lineNum) {
         Alignment paraAlign = mAlignment;
-        TabStops tabStops = null;
-        boolean tabStopsIsInitialized = false;
 
         int left = 0;
         int right = mWidth;
@@ -371,10 +369,6 @@
             }
         }
 
-        if (getLineContainsTab(lineNum)) {
-            tabStops = new TabStops(TAB_INCREMENT, spans);
-        }
-
         final Alignment align;
         if (paraAlign == Alignment.ALIGN_LEFT) {
             align = (dir == DIR_LEFT_TO_RIGHT) ?  Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
@@ -1423,7 +1417,6 @@
         float dist = Math.abs(getHorizontal(max, primary) - horiz);
 
         if (dist <= bestdist) {
-            bestdist = dist;
             best = max;
         }
 
@@ -1570,7 +1563,7 @@
         // XXX: we don't care about tabs
         tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
         caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
-        tl = TextLine.recycle(tl);
+        TextLine.recycle(tl);
         return caret;
     }
 
@@ -1894,10 +1887,7 @@
 
         int margin = 0;
 
-        boolean isFirstParaLine = lineStart == 0 ||
-            spanned.charAt(lineStart - 1) == '\n';
-
-        boolean useFirstLineMargin = isFirstParaLine;
+        boolean useFirstLineMargin = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
         for (int i = 0; i < spans.length; i++) {
             if (spans[i] instanceof LeadingMarginSpan2) {
                 int spStart = spanned.getSpanStart(spans[i]);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 961cd8ee..4b6b6ae 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -433,7 +433,6 @@
          *    + addStyleRun (a text run, to be measured in native code)
          *    + addReplacementRun (a replacement run, width is given)
          *
-         * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
          * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
          *
          * After all paragraphs, call finish() to release expensive buffers.
@@ -441,8 +440,6 @@
 
         private Pair<String, long[]> getLocaleAndHyphenatorIfChanged(TextPaint paint) {
             final LocaleList locales = paint.getTextLocales();
-            final String languageTags;
-            long[] hyphenators;
             if (!locales.equals(mLocales)) {
                 mLocales = locales;
                 return new Pair(locales.toLanguageTags(), getHyphenators(locales));
@@ -521,7 +518,7 @@
 
         private LocaleList mLocales;
 
-        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
+        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
     }
 
     public StaticLayout(CharSequence source, TextPaint paint,
@@ -866,10 +863,9 @@
                 spanEndCacheCount++;
             }
 
-            nGetWidths(b.mNativePtr, widths);
             int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
                     lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags,
-                    lineBreaks.breaks.length);
+                    lineBreaks.breaks.length, widths);
 
             final int[] breaks = lineBreaks.breaks;
             final float[] lineWidths = lineBreaks.widths;
@@ -947,10 +943,10 @@
                     boolean moreChars = (endPos < bufEnd);
 
                     final int ascent = fallbackLineSpacing
-                            ? Math.min(fmAscent, (int) Math.round(ascents[breakIndex]))
+                            ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
                             : fmAscent;
                     final int descent = fallbackLineSpacing
-                            ? Math.max(fmDescent, (int) Math.round(descents[breakIndex]))
+                            ? Math.max(fmDescent, Math.round(descents[breakIndex]))
                             : fmDescent;
                     v = out(source, here, endPos,
                             ascent, descent, fmTop, fmBottom,
@@ -1177,7 +1173,7 @@
         mWorkPaint.set(paint);
         do {
             final float ellipsizedWidth = guessEllipsis(text, lineStart, lineEnd, widths,
-                    widthStart, tempAvail, where, line, textWidth, mWorkPaint, forceEllipsis, dir);
+                    widthStart, tempAvail, where, line, mWorkPaint, forceEllipsis, dir);
             if (ellipsizedWidth <= avail) {
                 lineFits = true;
             } else {
@@ -1207,7 +1203,7 @@
     // This method temporarily modifies the TextPaint passed to it, so the TextPaint passed to it
     // should not be accessed while the method is running.
     private float guessEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths,
-            int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth,
+            int widthStart, float avail, TextUtils.TruncateAt where, int line,
             TextPaint paint, boolean forceEllipsis, int dir) {
         final int savedHyphenEdit = paint.getHyphenEdit();
         paint.setHyphenEdit(0);
@@ -1551,16 +1547,17 @@
             @FloatRange(from = 0.0f) float width, @Nullable String languageTags,
             @Nullable long[] hyphenators);
 
-    private static native void nGetWidths(long nativePtr, float[] widths);
-
     // populates LineBreaks and returns the number of breaks found
     //
     // the arrays inside the LineBreaks objects are passed in as well
     // to reduce the number of JNI calls in the common case where the
     // arrays do not have to be resized
+    // The individual character widths will be returned in charWidths. The length of charWidths must
+    // be at least the length of the text.
     private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
             int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents,
-            float[] recycleDescents, int[] recycleFlags, int recycleLength);
+            float[] recycleDescents, int[] recycleFlags, int recycleLength,
+            float[] charWidths);
 
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 2dbff10..20c0ed8 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -73,7 +73,7 @@
             new SpanSet<ReplacementSpan>(ReplacementSpan.class);
 
     private final DecorationInfo mDecorationInfo = new DecorationInfo();
-    private final ArrayList<DecorationInfo> mDecorations = new ArrayList();
+    private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>();
 
     private static final TextLine[] sCached = new TextLine[3];
 
@@ -340,14 +340,14 @@
 
                     boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
                     if (inSegment && advance) {
-                        return h += measureRun(segstart, offset, j, runIsRtl, fmi);
+                        return h + measureRun(segstart, offset, j, runIsRtl, fmi);
                     }
 
                     float w = measureRun(segstart, j, j, runIsRtl, fmi);
                     h += advance ? w : -w;
 
                     if (inSegment) {
-                        return h += measureRun(segstart, offset, j, runIsRtl, null);
+                        return h + measureRun(segstart, offset, j, runIsRtl, null);
                     }
 
                     if (codept == '\t') {
@@ -828,14 +828,14 @@
                     }
                     if (info.isUnderlineText) {
                         final float thickness =
-                                Math.max(((Paint) wp).getUnderlineThickness(), 1.0f);
+                                Math.max(wp.getUnderlineThickness(), 1.0f);
                         drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness,
                                 decorationXLeft, decorationXRight, y);
                     }
 
                     if (info.isStrikeThruText) {
                         final float thickness =
-                                Math.max(((Paint) wp).getStrikeThruThickness(), 1.0f);
+                                Math.max(wp.getStrikeThruThickness(), 1.0f);
                         drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness,
                                 decorationXLeft, decorationXRight, y);
                     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 31daeff..5482589 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -21,15 +21,22 @@
 import android.annotation.Size;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Binder;
+import android.os.Debug;
 import android.os.IBinder;
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
 
 import dalvik.system.CloseGuard;
 
+import java.io.Closeable;
+
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * SurfaceControl
  *  @hide
@@ -54,25 +61,34 @@
             Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
             boolean allLayers, boolean useIdentityTransform);
 
-    private static native void nativeOpenTransaction();
-    private static native void nativeCloseTransaction(boolean sync);
-    private static native void nativeSetAnimationTransaction();
+    private static native long nativeCreateTransaction();
+    private static native long nativeGetNativeTransactionFinalizer();
+    private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+    private static native void nativeSetAnimationTransaction(long transactionObj);
 
-    private static native void nativeSetLayer(long nativeObject, int zorder);
-    private static native void nativeSetRelativeLayer(long nativeObject, IBinder relativeTo,
-            int zorder);
-    private static native void nativeSetPosition(long nativeObject, float x, float y);
-    private static native void nativeSetGeometryAppliesWithResize(long nativeObject);
-    private static native void nativeSetSize(long nativeObject, int w, int h);
-    private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
-    private static native void nativeSetAlpha(long nativeObject, float alpha);
-    private static native void nativeSetColor(long nativeObject, float[] color);
-    private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
+    private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
+    private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
+            IBinder relativeTo, int zorder);
+    private static native void nativeSetPosition(long transactionObj, long nativeObject,
+            float x, float y);
+    private static native void nativeSetGeometryAppliesWithResize(long transactionObj,
+            long nativeObject);
+    private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
+    private static native void nativeSetTransparentRegionHint(long transactionObj,
+            long nativeObject, Region region);
+    private static native void nativeSetAlpha(long transactionObj, long nativeObject, float alpha);
+    private static native void nativeSetMatrix(long transactionObj, long nativeObject,
+            float dsdx, float dtdx,
             float dtdy, float dsdy);
-    private static native void nativeSetFlags(long nativeObject, int flags, int mask);
-    private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
-    private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b);
-    private static native void nativeSetLayerStack(long nativeObject, int layerStack);
+    private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
+    private static native void nativeSetFlags(long transactionObj, long nativeObject,
+            int flags, int mask);
+    private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
+            int l, int t, int r, int b);
+    private static native void nativeSetFinalCrop(long transactionObj, long nativeObject,
+            int l, int t, int r, int b);
+    private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
+            int layerStack);
 
     private static native boolean nativeClearContentFrameStats(long nativeObject);
     private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -82,15 +98,16 @@
     private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
     private static native IBinder nativeCreateDisplay(String name, boolean secure);
     private static native void nativeDestroyDisplay(IBinder displayToken);
-    private static native void nativeSetDisplaySurface(
+    private static native void nativeSetDisplaySurface(long transactionObj,
             IBinder displayToken, long nativeSurfaceObject);
-    private static native void nativeSetDisplayLayerStack(
+    private static native void nativeSetDisplayLayerStack(long transactionObj,
             IBinder displayToken, int layerStack);
-    private static native void nativeSetDisplayProjection(
+    private static native void nativeSetDisplayProjection(long transactionObj,
             IBinder displayToken, int orientation,
             int l, int t, int r, int b,
             int L, int T, int R, int B);
-    private static native void nativeSetDisplaySize(IBinder displayToken, int width, int height);
+    private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
+            int width, int height);
     private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
             IBinder displayToken);
     private static native int nativeGetActiveConfig(IBinder displayToken);
@@ -101,16 +118,17 @@
             int colorMode);
     private static native void nativeSetDisplayPowerMode(
             IBinder displayToken, int mode);
-    private static native void nativeDeferTransactionUntil(long nativeObject,
+    private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
             IBinder handle, long frame);
-    private static native void nativeDeferTransactionUntilSurface(long nativeObject,
+    private static native void nativeDeferTransactionUntilSurface(long transactionObj,
+            long nativeObject,
             long surfaceObject, long frame);
-    private static native void nativeReparentChildren(long nativeObject,
+    private static native void nativeReparentChildren(long transactionObj, long nativeObject,
             IBinder handle);
-    private static native void nativeReparent(long nativeObject,
+    private static native void nativeReparent(long transactionObj, long nativeObject,
             IBinder parentHandle);
-    private static native void nativeSeverChildren(long nativeObject);
-    private static native void nativeSetOverrideScalingMode(long nativeObject,
+    private static native void nativeSeverChildren(long transactionObj, long nativeObject);
+    private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
     private static native IBinder nativeGetHandle(long nativeObject);
     private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -122,6 +140,9 @@
     private final String mName;
     long mNativeObject; // package visibility only for Surface.java access
 
+    static Transaction sGlobalTransaction;
+    static long sTransactionNestCount = 0;
+
     /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
 
     /**
@@ -377,11 +398,6 @@
         }
     }
 
-    @Override
-    public String toString() {
-        return "Surface(name=" + mName + ")";
-    }
-
     /**
      * Release the local reference to the server-side surface.
      * Always call release() when you're done with a Surface.
@@ -429,102 +445,141 @@
 
     /** start a transaction */
     public static void openTransaction() {
-        nativeOpenTransaction();
+        synchronized (SurfaceControl.class) {
+            if (sGlobalTransaction == null) {
+                sGlobalTransaction = new Transaction();
+            }
+            synchronized(SurfaceControl.class) {
+                sTransactionNestCount++;
+            }
+        }
+    }
+
+    private static void closeTransaction(boolean sync) {
+        synchronized(SurfaceControl.class) {
+            if (sTransactionNestCount == 0) {
+                Log.e(TAG, "Call to SurfaceControl.closeTransaction without matching openTransaction");
+            } else if (--sTransactionNestCount > 0) {
+                return;
+            }
+            sGlobalTransaction.apply(sync);
+        }
     }
 
     /** end a transaction */
     public static void closeTransaction() {
-        nativeCloseTransaction(false);
+        closeTransaction(false);
     }
 
     public static void closeTransactionSync() {
-        nativeCloseTransaction(true);
+        closeTransaction(true);
     }
 
     public void deferTransactionUntil(IBinder handle, long frame) {
         if (frame > 0) {
-            nativeDeferTransactionUntil(mNativeObject, handle, frame);
+            synchronized(SurfaceControl.class) {
+                sGlobalTransaction.deferTransactionUntil(this, handle, frame);
+            }
         }
     }
 
     public void deferTransactionUntil(Surface barrier, long frame) {
         if (frame > 0) {
-            nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
+            synchronized(SurfaceControl.class) {
+                sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
+            }
         }
     }
 
     public void reparentChildren(IBinder newParentHandle) {
-        nativeReparentChildren(mNativeObject, newParentHandle);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.reparentChildren(this, newParentHandle);
+        }
     }
 
-    /** Re-parents this layer to a new parent. */
     public void reparent(IBinder newParentHandle) {
-        nativeReparent(mNativeObject, newParentHandle);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.reparent(this, newParentHandle);
+        }
     }
 
     public void detachChildren() {
-        nativeSeverChildren(mNativeObject);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.detachChildren(this);
+        }
     }
 
     public void setOverrideScalingMode(int scalingMode) {
         checkNotReleased();
-        nativeSetOverrideScalingMode(mNativeObject, scalingMode);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setOverrideScalingMode(this, scalingMode);
+        }
     }
 
     public IBinder getHandle() {
         return nativeGetHandle(mNativeObject);
     }
 
-    /** flag the transaction as an animation */
     public static void setAnimationTransaction() {
-        nativeSetAnimationTransaction();
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setAnimationTransaction();
+        }
     }
 
     public void setLayer(int zorder) {
         checkNotReleased();
-        nativeSetLayer(mNativeObject, zorder);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setLayer(this, zorder);
+        }
     }
 
     public void setRelativeLayer(IBinder relativeTo, int zorder) {
         checkNotReleased();
-        nativeSetRelativeLayer(mNativeObject, relativeTo, zorder);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder);
+        }
     }
 
     public void setPosition(float x, float y) {
         checkNotReleased();
-        nativeSetPosition(mNativeObject, x, y);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setPosition(this, x, y);
+        }
     }
 
-    /**
-     * If the buffer size changes in this transaction, position and crop updates specified
-     * in this transaction will not complete until a buffer of the new size
-     * arrives. As transform matrix and size are already frozen in this fashion,
-     * this enables totally freezing the surface until the resize has completed
-     * (at which point the geometry influencing aspects of this transaction will then occur)
-     */
     public void setGeometryAppliesWithResize() {
         checkNotReleased();
-        nativeSetGeometryAppliesWithResize(mNativeObject);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setGeometryAppliesWithResize(this);
+        }
     }
 
     public void setSize(int w, int h) {
         checkNotReleased();
-        nativeSetSize(mNativeObject, w, h);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setSize(this, w, h);
+        }
     }
 
     public void hide() {
         checkNotReleased();
-        nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.hide(this);
+        }
     }
 
     public void show() {
         checkNotReleased();
-        nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.show(this);
+        }
     }
 
     public void setTransparentRegionHint(Region region) {
         checkNotReleased();
-        nativeSetTransparentRegionHint(mNativeObject, region);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setTransparentRegionHint(this, region);
+        }
     }
 
     public boolean clearContentFrameStats() {
@@ -545,80 +600,70 @@
         return nativeGetAnimationFrameStats(outStats);
     }
 
-    /**
-     * Sets an alpha value for the entire Surface.  This value is combined with the
-     * per-pixel alpha.  It may be used with opaque Surfaces.
-     */
     public void setAlpha(float alpha) {
         checkNotReleased();
-        nativeSetAlpha(mNativeObject, alpha);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setAlpha(this, alpha);
+        }
     }
 
-    /**
-     * Sets a color for the Surface.
-     * @param color A float array with three values to represent r, g, b in range [0..1]
-     */
     public void setColor(@Size(3) float[] color) {
         checkNotReleased();
-        nativeSetColor(mNativeObject, color);
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setColor(this, color);
+        }
     }
 
     public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
         checkNotReleased();
-        nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setMatrix(this, dsdx, dtdx, dtdy, dsdy);
+        }
     }
 
     public void setWindowCrop(Rect crop) {
         checkNotReleased();
-        if (crop != null) {
-            nativeSetWindowCrop(mNativeObject,
-                crop.left, crop.top, crop.right, crop.bottom);
-        } else {
-            nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0);
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setWindowCrop(this, crop);
         }
     }
 
     public void setFinalCrop(Rect crop) {
         checkNotReleased();
-        if (crop != null) {
-            nativeSetFinalCrop(mNativeObject,
-                crop.left, crop.top, crop.right, crop.bottom);
-        } else {
-            nativeSetFinalCrop(mNativeObject, 0, 0, 0, 0);
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setFinalCrop(this, crop);
         }
     }
 
     public void setLayerStack(int layerStack) {
         checkNotReleased();
-        nativeSetLayerStack(mNativeObject, layerStack);
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.setLayerStack(this, layerStack);
+        }
     }
 
-    /**
-     * Sets the opacity of the surface.  Setting the flag is equivalent to creating the
-     * Surface with the {@link #OPAQUE} flag.
-     */
     public void setOpaque(boolean isOpaque) {
         checkNotReleased();
-        if (isOpaque) {
-            nativeSetFlags(mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
-        } else {
-            nativeSetFlags(mNativeObject, 0, SURFACE_OPAQUE);
+
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setOpaque(this, isOpaque);
         }
     }
 
-    /**
-     * Sets the security of the surface.  Setting the flag is equivalent to creating the
-     * Surface with the {@link #SECURE} flag.
-     */
     public void setSecure(boolean isSecure) {
         checkNotReleased();
-        if (isSecure) {
-            nativeSetFlags(mNativeObject, SECURE, SECURE);
-        } else {
-            nativeSetFlags(mNativeObject, 0, SECURE);
+
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setSecure(this, isSecure);
         }
     }
 
+    @Override
+    public String toString() {
+        return "Surface(name=" + mName + ")/@0x" +
+                Integer.toHexString(System.identityHashCode(this));
+    }
+
     /*
      * set display parameters.
      * needs to be inside open/closeTransaction block
@@ -741,50 +786,28 @@
 
     public static void setDisplayProjection(IBinder displayToken,
             int orientation, Rect layerStackRect, Rect displayRect) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setDisplayProjection(displayToken, orientation,
+                    layerStackRect, displayRect);
         }
-        if (layerStackRect == null) {
-            throw new IllegalArgumentException("layerStackRect must not be null");
-        }
-        if (displayRect == null) {
-            throw new IllegalArgumentException("displayRect must not be null");
-        }
-        nativeSetDisplayProjection(displayToken, orientation,
-                layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
-                displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
     }
 
     public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setDisplayLayerStack(displayToken, layerStack);
         }
-        nativeSetDisplayLayerStack(displayToken, layerStack);
     }
 
     public static void setDisplaySurface(IBinder displayToken, Surface surface) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
-        }
-
-        if (surface != null) {
-            synchronized (surface.mLock) {
-                nativeSetDisplaySurface(displayToken, surface.mNativeObject);
-            }
-        } else {
-            nativeSetDisplaySurface(displayToken, 0);
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setDisplaySurface(displayToken, surface);
         }
     }
 
     public static void setDisplaySize(IBinder displayToken, int width, int height) {
-        if (displayToken == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setDisplaySize(displayToken, width, height);
         }
-        if (width <= 0 || height <= 0) {
-            throw new IllegalArgumentException("width and height must be positive");
-        }
-
-        nativeSetDisplaySize(displayToken, width, height);
     }
 
     public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) {
@@ -946,4 +969,261 @@
         nativeScreenshot(display, consumer, sourceCrop, width, height,
                 minLayer, maxLayer, allLayers, useIdentityTransform);
     }
+
+    public static class Transaction implements Closeable {
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Transaction.class.getClassLoader(),
+                nativeGetNativeTransactionFinalizer(), 512);
+        private long mNativeObject;
+
+        Runnable mFreeNativeResources;
+
+        public Transaction() {
+            mNativeObject = nativeCreateTransaction();
+            mFreeNativeResources
+                = sRegistry.registerNativeAllocation(this, mNativeObject);
+        }
+
+        /**
+         * Apply the transaction, clearing it's state, and making it usable
+         * as a new transaction.
+         */
+        public void apply() {
+            apply(false);
+        }
+
+        /**
+         * Close the transaction, if the transaction was not already applied this will cancel the
+         * transaction.
+         */
+        @Override
+        public void close() {
+            mFreeNativeResources.run();
+            mNativeObject = 0;
+        }
+
+        /**
+         * Jankier version of apply. Avoid use (b/28068298).
+         */
+        public void apply(boolean sync) {
+            nativeApplyTransaction(mNativeObject, sync);
+        }
+
+        public Transaction show(SurfaceControl sc) {
+            nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
+            return this;
+        }
+
+        public Transaction hide(SurfaceControl sc) {
+            nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
+            return this;
+        }
+
+        public Transaction setPosition(SurfaceControl sc, float x, float y) {
+            nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
+            return this;
+        }
+
+        public Transaction setSize(SurfaceControl sc, int w, int h) {
+            nativeSetSize(mNativeObject, sc.mNativeObject,
+                    w, h);
+            return this;
+        }
+
+        public Transaction setLayer(SurfaceControl sc, int z) {
+            nativeSetLayer(mNativeObject, sc.mNativeObject, z);
+            return this;
+        }
+
+        public Transaction setRelativeLayer(SurfaceControl sc, IBinder relativeTo, int z) {
+            nativeSetRelativeLayer(mNativeObject, sc.mNativeObject,
+                    relativeTo, z);
+            return this;
+        }
+
+        public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
+            nativeSetTransparentRegionHint(mNativeObject,
+                    sc.mNativeObject, transparentRegion);
+            return this;
+        }
+
+        public Transaction setAlpha(SurfaceControl sc, float alpha) {
+            nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
+            return this;
+        }
+
+        public Transaction setMatrix(SurfaceControl sc,
+                float dsdx, float dtdx, float dtdy, float dsdy) {
+            nativeSetMatrix(mNativeObject, sc.mNativeObject,
+                    dsdx, dtdx, dtdy, dsdy);
+            return this;
+        }
+
+        public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
+            if (crop != null) {
+                nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
+                        crop.left, crop.top, crop.right, crop.bottom);
+            } else {
+                nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
+            }
+
+            return this;
+        }
+
+        public Transaction setFinalCrop(SurfaceControl sc, Rect crop) {
+            if (crop != null) {
+                nativeSetFinalCrop(mNativeObject, sc.mNativeObject,
+                        crop.left, crop.top, crop.right, crop.bottom);
+            } else {
+                nativeSetFinalCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
+            }
+
+            return this;
+        }
+
+        public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
+            nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
+            return this;
+        }
+
+        public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle, long frameNumber) {
+            nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
+            return this;
+        }
+
+        public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
+                long frameNumber) {
+            nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
+                    barrierSurface.mNativeObject, frameNumber);
+            return this;
+        }
+
+        public Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) {
+            nativeReparentChildren(mNativeObject, sc.mNativeObject, newParentHandle);
+            return this;
+        }
+
+        /** Re-parents a specific child layer to a new parent */
+        public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
+            nativeReparent(mNativeObject, sc.mNativeObject,
+                    newParentHandle);
+            return this;
+        }
+
+        public Transaction detachChildren(SurfaceControl sc) {
+            nativeSeverChildren(mNativeObject, sc.mNativeObject);
+            return this;
+        }
+
+        public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
+            nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
+                    overrideScalingMode);
+            return this;
+        }
+
+        /**
+         * Sets a color for the Surface.
+         * @param color A float array with three values to represent r, g, b in range [0..1]
+         */
+        public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) {
+            nativeSetColor(mNativeObject, sc.mNativeObject, color);
+            return this;
+        }
+
+        /**
+         * If the buffer size changes in this transaction, position and crop updates specified
+         * in this transaction will not complete until a buffer of the new size
+         * arrives. As transform matrix and size are already frozen in this fashion,
+         * this enables totally freezing the surface until the resize has completed
+         * (at which point the geometry influencing aspects of this transaction will then occur)
+         */
+        public Transaction setGeometryAppliesWithResize(SurfaceControl sc) {
+            nativeSetGeometryAppliesWithResize(mNativeObject, sc.mNativeObject);
+            return this;
+        }
+
+        /**
+         * Sets the security of the surface.  Setting the flag is equivalent to creating the
+         * Surface with the {@link #SECURE} flag.
+         */
+        Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+            if (isSecure) {
+                nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
+            } else {
+                nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SECURE);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the opacity of the surface.  Setting the flag is equivalent to creating the
+         * Surface with the {@link #OPAQUE} flag.
+         */
+        public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
+            if (isOpaque) {
+                nativeSetFlags(mNativeObject, sc.mNativeObject, OPAQUE, OPAQUE);
+            } else {
+                nativeSetFlags(mNativeObject, sc.mNativeObject, 0, OPAQUE);
+            }
+            return this;
+        }
+
+        public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
+            if (displayToken == null) {
+                throw new IllegalArgumentException("displayToken must not be null");
+            }
+
+            if (surface != null) {
+                synchronized (surface.mLock) {
+                    nativeSetDisplaySurface(mNativeObject, displayToken, surface.mNativeObject);
+                }
+            } else {
+                nativeSetDisplaySurface(mNativeObject, displayToken, 0);
+            }
+            return this;
+        }
+
+        public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
+            if (displayToken == null) {
+                throw new IllegalArgumentException("displayToken must not be null");
+            }
+            nativeSetDisplayLayerStack(mNativeObject, displayToken, layerStack);
+            return this;
+        }
+
+        public Transaction setDisplayProjection(IBinder displayToken,
+                int orientation, Rect layerStackRect, Rect displayRect) {
+            if (displayToken == null) {
+                throw new IllegalArgumentException("displayToken must not be null");
+            }
+            if (layerStackRect == null) {
+                throw new IllegalArgumentException("layerStackRect must not be null");
+            }
+            if (displayRect == null) {
+                throw new IllegalArgumentException("displayRect must not be null");
+            }
+            nativeSetDisplayProjection(mNativeObject, displayToken, orientation,
+                    layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
+                    displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
+            return this;
+        }
+
+        public Transaction setDisplaySize(IBinder displayToken, int width, int height) {
+            if (displayToken == null) {
+                throw new IllegalArgumentException("displayToken must not be null");
+            }
+            if (width <= 0 || height <= 0) {
+                throw new IllegalArgumentException("width and height must be positive");
+            }
+
+            nativeSetDisplaySize(mNativeObject, displayToken, width, height);
+            return this;
+        }
+
+        /** flag the transaction as an animation */
+        public Transaction setAnimationTransaction() {
+            nativeSetAnimationTransaction(mNativeObject);
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b6be296..0d1258d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1448,17 +1448,59 @@
 
     /**
      * <p>Enables low quality mode for the drawing cache.</p>
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000;
 
     /**
      * <p>Enables high quality mode for the drawing cache.</p>
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000;
 
     /**
      * <p>Enables automatic quality mode for the drawing cache.</p>
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000;
 
     private static final int[] DRAWING_CACHE_QUALITY_FLAGS = {
@@ -8907,7 +8949,21 @@
      * @see #isDrawingCacheEnabled()
      *
      * @attr ref android.R.styleable#View_drawingCacheQuality
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     @DrawingCacheQuality
     public int getDrawingCacheQuality() {
         return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
@@ -8925,7 +8981,21 @@
      * @see #isDrawingCacheEnabled()
      *
      * @attr ref android.R.styleable#View_drawingCacheQuality
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void setDrawingCacheQuality(@DrawingCacheQuality int quality) {
         setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
     }
@@ -18103,7 +18173,21 @@
      * @see #getDrawingCache()
      * @see #buildDrawingCache()
      * @see #setLayerType(int, android.graphics.Paint)
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void setDrawingCacheEnabled(boolean enabled) {
         mCachingFailed = false;
         setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
@@ -18116,7 +18200,21 @@
      *
      * @see #setDrawingCacheEnabled(boolean)
      * @see #getDrawingCache()
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isDrawingCacheEnabled() {
         return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
@@ -18257,7 +18355,21 @@
      * @return A non-scaled bitmap representing this view or null if cache is disabled.
      *
      * @see #getDrawingCache(boolean)
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public Bitmap getDrawingCache() {
         return getDrawingCache(false);
     }
@@ -18288,7 +18400,21 @@
      * @see #isDrawingCacheEnabled()
      * @see #buildDrawingCache(boolean)
      * @see #destroyDrawingCache()
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public Bitmap getDrawingCache(boolean autoScale) {
         if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
             return null;
@@ -18308,7 +18434,21 @@
      * @see #setDrawingCacheEnabled(boolean)
      * @see #buildDrawingCache()
      * @see #getDrawingCache()
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void destroyDrawingCache() {
         if (mDrawingCache != null) {
             mDrawingCache.recycle();
@@ -18330,7 +18470,21 @@
      * @see #setDrawingCacheEnabled(boolean)
      * @see #buildDrawingCache()
      * @see #getDrawingCache()
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void setDrawingCacheBackgroundColor(@ColorInt int color) {
         if (color != mDrawingCacheBackgroundColor) {
             mDrawingCacheBackgroundColor = color;
@@ -18342,7 +18496,21 @@
      * @see #setDrawingCacheBackgroundColor(int)
      *
      * @return The background color to used for the drawing cache's bitmap
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     @ColorInt
     public int getDrawingCacheBackgroundColor() {
         return mDrawingCacheBackgroundColor;
@@ -18352,7 +18520,21 @@
      * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p>
      *
      * @see #buildDrawingCache(boolean)
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void buildDrawingCache() {
         buildDrawingCache(false);
     }
@@ -18379,7 +18561,21 @@
      *
      * @see #getDrawingCache()
      * @see #destroyDrawingCache()
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b2e5a16..929beae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -421,22 +421,78 @@
 
     /**
      * Used to indicate that no drawing cache should be kept in memory.
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int PERSISTENT_NO_CACHE = 0x0;
 
     /**
      * Used to indicate that the animation drawing cache should be kept in memory.
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
 
     /**
      * Used to indicate that the scrolling drawing cache should be kept in memory.
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
 
     /**
      * Used to indicate that all drawing caches should be kept in memory.
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public static final int PERSISTENT_ALL_CACHES = 0x3;
 
     // Layout Modes
@@ -3769,7 +3825,21 @@
      * Enables or disables the drawing cache for each child of this view group.
      *
      * @param enabled true to enable the cache, false to dispose of it
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
         if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
             final View[] children = mChildren;
@@ -6331,7 +6401,21 @@
      * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
      *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
      *         and {@link #PERSISTENT_ALL_CACHES}
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     @ViewDebug.ExportedProperty(category = "drawing", mapping = {
         @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
         @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@@ -6352,7 +6436,21 @@
      * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
      *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
      *        and {@link #PERSISTENT_ALL_CACHES}
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void setPersistentDrawingCache(int drawingCacheToKeep) {
         mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
     }
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 98f8dc8..69cc100 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -335,8 +335,8 @@
     public abstract void setOnHardKeyboardStatusChangeListener(
         OnHardKeyboardStatusChangeListener listener);
 
-    /** Returns true if the stack with the input Id is currently visible. */
-    public abstract boolean isStackVisible(int stackId);
+    /** Returns true if a stack in the windowing mode is currently visible. */
+    public abstract boolean isStackVisible(int windowingMode);
 
     /**
      * @return True if and only if the docked divider is currently in resize mode.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index da72535..137e551 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -467,11 +467,8 @@
          */
         public boolean isDimming();
 
-        /**
-         * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
-         *         not attached to any stack.
-         */
-        int getStackId();
+        /** @return the current windowing mode of this window. */
+        int getWindowingMode();
 
         /**
          * Returns true if the window is current in multi-windowing mode. i.e. it shares the
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 0f21c5c..d785117 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -329,8 +329,6 @@
                 final long oldParentId = oldInfo.getParentNodeId();
                 if (info.getParentNodeId() != oldParentId) {
                     clearSubTreeLocked(windowId, oldParentId);
-                } else {
-                    oldInfo.recycle();
                 }
            }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 4fb2a99..e2022de 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -150,6 +150,12 @@
      * service authentication will contain the Bundle set by
      * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
      *
+     * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
+     * can also add this bundle to the {@link Intent} set as the
+     * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
+     * so the bundle can be recovered later on
+     * {@link android.service.autofill.SaveRequest#getClientState()}.
+     *
      * <p>
      * Type: {@link android.os.Bundle}
      */
@@ -959,6 +965,10 @@
             final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
             final Bundle responseData = new Bundle();
             responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
+            final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
+            if (newClientState != null) {
+                responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
+            }
             try {
                 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
                         mContext.getUserId());
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index 5b92a17..bb75bf6 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -37,6 +37,12 @@
 
     private ArrayList<AlsaCardRecord> mCardRecords = new ArrayList<AlsaCardRecord>();
 
+    public static final int SCANSTATUS_NOTSCANNED = -1;
+    public static final int SCANSTATUS_SUCCESS = 0;
+    public static final int SCANSTATUS_FAIL = 1;
+    public static final int SCANSTATUS_EMPTY = 2;
+    private int mScanStatus = SCANSTATUS_NOTSCANNED;
+
     public class AlsaCardRecord {
         private static final String TAG = "AlsaCardRecord";
         private static final String kUsbCardKeyStr = "at usb-";
@@ -104,10 +110,11 @@
 
     public AlsaCardsParser() {}
 
-    public void scan() {
+    public int scan() {
         if (DEBUG) {
-            Slog.i(TAG, "AlsaCardsParser.scan()");
+            Slog.i(TAG, "AlsaCardsParser.scan()....");
         }
+
         mCardRecords = new ArrayList<AlsaCardRecord>();
 
         File cardsFile = new File(kCardsFilePath);
@@ -134,11 +141,26 @@
                 mCardRecords.add(cardRecord);
             }
             reader.close();
+            if (mCardRecords.size() > 0) {
+                mScanStatus = SCANSTATUS_SUCCESS;
+            } else {
+                mScanStatus = SCANSTATUS_EMPTY;
+            }
         } catch (FileNotFoundException e) {
             e.printStackTrace();
+            mScanStatus = SCANSTATUS_FAIL;
         } catch (IOException e) {
             e.printStackTrace();
+            mScanStatus = SCANSTATUS_FAIL;
         }
+        if (DEBUG) {
+            Slog.i(TAG, "  status:" + mScanStatus);
+        }
+        return mScanStatus;
+    }
+
+    public int getScanStatus() {
+        return mScanStatus;
     }
 
     public ArrayList<AlsaCardRecord> getScanRecords() {
@@ -182,7 +204,11 @@
         }
 
         // get the new list of devices
-        scan();
+        if (scan() != SCANSTATUS_SUCCESS) {
+            Slog.e(TAG, "Error scanning Alsa cards file.");
+            return -1;
+        }
+
         if (DEBUG) {
             LogDevices("Current Devices:", mCardRecords);
         }
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 6e3d596..15261baf 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -46,6 +46,12 @@
     private boolean mHasPlaybackDevices = false;
     private boolean mHasMIDIDevices = false;
 
+    public static final int SCANSTATUS_NOTSCANNED = -1;
+    public static final int SCANSTATUS_SUCCESS = 0;
+    public static final int SCANSTATUS_FAIL = 1;
+    public static final int SCANSTATUS_EMPTY = 2;
+    private int mScanStatus = SCANSTATUS_NOTSCANNED;
+
     public class AlsaDeviceRecord {
         public static final int kDeviceType_Unknown = -1;
         public static final int kDeviceType_Audio = 0;
@@ -258,7 +264,11 @@
         return line.charAt(kIndex_CardDeviceField) == '[';
     }
 
-    public boolean scan() {
+    public int scan() {
+        if (DEBUG) {
+            Slog.i(TAG, "AlsaDevicesParser.scan()....");
+        }
+
         mDeviceRecords.clear();
 
         File devicesFile = new File(kDevicesFilePath);
@@ -274,13 +284,27 @@
                 }
             }
             reader.close();
-            return true;
+            // success if we add at least 1 record
+            if (mDeviceRecords.size() > 0) {
+                mScanStatus = SCANSTATUS_SUCCESS;
+            } else {
+                mScanStatus = SCANSTATUS_EMPTY;
+            }
         } catch (FileNotFoundException e) {
             e.printStackTrace();
+            mScanStatus = SCANSTATUS_FAIL;
         } catch (IOException e) {
             e.printStackTrace();
+            mScanStatus = SCANSTATUS_FAIL;
         }
-        return false;
+        if (DEBUG) {
+            Slog.i(TAG, "  status:" + mScanStatus);
+        }
+        return mScanStatus;
+    }
+
+    public int getScanStatus() {
+        return mScanStatus;
     }
 
     //
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index d64c9a1..4a181b2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -20,6 +20,7 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
 import android.os.RemoteException;
 import android.provider.Settings;
 
@@ -47,6 +48,7 @@
     public static String RETAIL_MODE = "RETAIL_MODE";
     public static String USB = "USB";
     public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
+    public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
 
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -139,6 +141,17 @@
         foregroundChannel.setBlockableSystem(true);
         channelsList.add(foregroundChannel);
 
+        NotificationChannel heavyWeightChannel = new NotificationChannel(
+                HEAVY_WEIGHT_APP,
+                context.getString(R.string.notification_channel_heavy_weight_app),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        heavyWeightChannel.setShowBadge(false);
+        heavyWeightChannel.setSound(null, new AudioAttributes.Builder()
+                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+                .build());
+        channelsList.add(heavyWeightChannel);
+
         nm.createNotificationChannels(channelsList);
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 36fd991..dd07ddb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -681,17 +681,17 @@
     }
 
     @Override
-    public long getMahDischarge(int which) {
+    public long getUahDischarge(int which) {
         return mDischargeCounter.getCountLocked(which);
     }
 
     @Override
-    public long getMahDischargeScreenOff(int which) {
+    public long getUahDischargeScreenOff(int which) {
         return mDischargeScreenOffCounter.getCountLocked(which);
     }
 
     @Override
-    public long getMahDischargeScreenDoze(int which) {
+    public long getUahDischargeScreenDoze(int which) {
         return mDischargeScreenDozeCounter.getCountLocked(which);
     }
 
@@ -3588,7 +3588,7 @@
 
     public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
             long realtime) {
-        final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState);
+        final boolean screenOff = !isScreenOn(screenState);
         final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
         final boolean updateOnBatteryScreenOffTimeBase =
                 (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -5427,6 +5427,10 @@
                 elapsedRealtimeUs, which);
     }
 
+    @Override public Timer getScreenBrightnessTimer(int brightnessBin) {
+        return mScreenBrightnessTimer[brightnessBin];
+    }
+
     @Override public long getInteractiveTime(long elapsedRealtimeUs, int which) {
         return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -5520,10 +5524,18 @@
                 elapsedRealtimeUs, which);
     }
 
+    @Override public Timer getPhoneSignalScanningTimer() {
+        return mPhoneSignalScanningTimer;
+    }
+
     @Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
         return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which);
     }
 
+    @Override public Timer getPhoneSignalStrengthTimer(int strengthBin) {
+        return mPhoneSignalStrengthsTimer[strengthBin];
+    }
+
     @Override public long getPhoneDataConnectionTime(int dataType,
             long elapsedRealtimeUs, int which) {
         return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
@@ -5534,6 +5546,10 @@
         return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
     }
 
+    @Override public Timer getPhoneDataConnectionTimer(int dataType) {
+        return mPhoneDataConnectionsTimer[dataType];
+    }
+
     @Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
         return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -5572,6 +5588,10 @@
         return mWifiStateTimer[wifiState].getCountLocked(which);
     }
 
+    @Override public Timer getWifiStateTimer(int wifiState) {
+        return mWifiStateTimer[wifiState];
+    }
+
     @Override public long getWifiSupplStateTime(int state,
             long elapsedRealtimeUs, int which) {
         return mWifiSupplStateTimer[state].getTotalTimeLocked(
@@ -5582,6 +5602,10 @@
         return mWifiSupplStateTimer[state].getCountLocked(which);
     }
 
+    @Override public Timer getWifiSupplStateTimer(int state) {
+        return mWifiSupplStateTimer[state];
+    }
+
     @Override public long getWifiSignalStrengthTime(int strengthBin,
             long elapsedRealtimeUs, int which) {
         return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
@@ -5592,6 +5616,10 @@
         return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
     }
 
+    @Override public Timer getWifiSignalStrengthTimer(int strengthBin) {
+        return mWifiSignalStrengthsTimer[strengthBin];
+    }
+
     @Override
     public ControllerActivityCounter getBluetoothControllerActivity() {
         return mBluetoothActivity;
@@ -9463,7 +9491,7 @@
     }
 
     public boolean isScreenOn(int state) {
-        return state == Display.STATE_ON;
+        return state == Display.STATE_ON || state == Display.STATE_VR;
     }
 
     public boolean isScreenOff(int state) {
@@ -12791,7 +12819,7 @@
         mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null,
                 mOnBatteryTimeBase, in);
-        mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null, 
+        mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
                 mOnBatteryTimeBase, in);
         mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
diff --git a/core/java/com/android/internal/os/LoggingPrintStream.java b/core/java/com/android/internal/os/LoggingPrintStream.java
index f14394a..d27874c 100644
--- a/core/java/com/android/internal/os/LoggingPrintStream.java
+++ b/core/java/com/android/internal/os/LoggingPrintStream.java
@@ -28,12 +28,15 @@
 import java.util.Formatter;
 import java.util.Locale;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * A print stream which logs output line by line.
  *
  * {@hide}
  */
-abstract class LoggingPrintStream extends PrintStream {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public abstract class LoggingPrintStream extends PrintStream {
 
     private final StringBuilder builder = new StringBuilder();
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index deb4d5a..95cb616 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -19,6 +19,8 @@
 #define LOG_NDEBUG 1
 
 #include <android_runtime/AndroidRuntime.h>
+
+#include <android-base/properties.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -47,8 +49,8 @@
 #include <string>
 #include <vector>
 
-
 using namespace android;
+using android::base::GetProperty;
 
 extern int register_android_os_Binder(JNIEnv* env);
 extern int register_android_os_Process(JNIEnv* env);
@@ -392,17 +394,6 @@
     return false;
 }
 
-// Convenience wrapper over the property API that returns an
-// std::string.
-std::string getProperty(const char* key, const char* defaultValue) {
-    std::vector<char> temp(PROPERTY_VALUE_MAX);
-    const int len = property_get(key, &temp[0], defaultValue);
-    if (len < 0) {
-        return "";
-    }
-    return std::string(&temp[0], len);
-}
-
 /*
  * Read the persistent locale. Inspects the following system properties
  * (in order) and returns the first non-empty property in the list :
@@ -419,15 +410,15 @@
  */
 const std::string readLocale()
 {
-    const std::string locale = getProperty("persist.sys.locale", "");
+    const std::string locale = GetProperty("persist.sys.locale", "");
     if (!locale.empty()) {
         return locale;
     }
 
-    const std::string language = getProperty("persist.sys.language", "");
+    const std::string language = GetProperty("persist.sys.language", "");
     if (!language.empty()) {
-        const std::string country = getProperty("persist.sys.country", "");
-        const std::string variant = getProperty("persist.sys.localevar", "");
+        const std::string country = GetProperty("persist.sys.country", "");
+        const std::string variant = GetProperty("persist.sys.localevar", "");
 
         std::string out = language;
         if (!country.empty()) {
@@ -441,15 +432,15 @@
         return out;
     }
 
-    const std::string productLocale = getProperty("ro.product.locale", "");
+    const std::string productLocale = GetProperty("ro.product.locale", "");
     if (!productLocale.empty()) {
         return productLocale;
     }
 
     // If persist.sys.locale and ro.product.locale are missing,
     // construct a locale value from the individual locale components.
-    const std::string productLanguage = getProperty("ro.product.locale.language", "en");
-    const std::string productRegion = getProperty("ro.product.locale.region", "US");
+    const std::string productLanguage = GetProperty("ro.product.locale.language", "en");
+    const std::string productRegion = GetProperty("ro.product.locale.region", "US");
 
     return productLanguage + "-" + productRegion;
 }
@@ -649,7 +640,7 @@
     char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];
     char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX];
     char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];
-    char fingerprintBuf[sizeof("-Xfingerprint:") + PROPERTY_VALUE_MAX];
+    std::string fingerprintBuf;
 
     bool checkJni = false;
     property_get("dalvik.vm.checkjni", propBuf, "");
@@ -964,8 +955,15 @@
     /*
      * Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will
      * contain the fingerprint and can be parsed.
+     * Fingerprints are potentially longer than PROPERTY_VALUE_MAX, so parseRuntimeOption() cannot
+     * be used here.
+     * Do not ever re-assign fingerprintBuf as its c_str() value is stored in mOptions.
      */
-    parseRuntimeOption("ro.build.fingerprint", fingerprintBuf, "-Xfingerprint:");
+    std::string fingerprint = GetProperty("ro.build.fingerprint", "");
+    if (!fingerprint.empty()) {
+        fingerprintBuf = "-Xfingerprint:" + fingerprint;
+        addOption(fingerprintBuf.c_str());
+    }
 
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index c8eef7f..1628220 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -23,13 +23,13 @@
 #include <vector>
 #include <cmath>
 
+#include <android-base/properties.h>
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
 #include <utils/String8.h>
-#include <cutils/properties.h>
 #include <system/camera_metadata.h>
 #include <camera/CameraMetadata.h>
 #include <img_utils/DngUtils.h>
@@ -50,6 +50,7 @@
 
 using namespace android;
 using namespace img_utils;
+using android::base::GetProperty;
 
 #define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
     if ((expr) != OK) { \
@@ -1237,26 +1238,24 @@
 
     {
         // make
-        char manufacturer[PROPERTY_VALUE_MAX];
-
         // Use "" to represent unknown make as suggested in TIFF/EP spec.
-        property_get("ro.product.manufacturer", manufacturer, "");
-        uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
+        std::string manufacturer = GetProperty("ro.product.manufacturer", "");
+        uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1;
 
         BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
-                reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
+                reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE,
+                writer);
     }
 
     {
         // model
-        char model[PROPERTY_VALUE_MAX];
-
         // Use "" to represent unknown model as suggested in TIFF/EP spec.
-        property_get("ro.product.model", model, "");
-        uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
+        std::string model = GetProperty("ro.product.model", "");
+        uint32_t count = static_cast<uint32_t>(model.size()) + 1;
 
         BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
-                reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
+                reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL,
+                writer);
     }
 
     {
@@ -1277,11 +1276,11 @@
 
     {
         // software
-        char software[PROPERTY_VALUE_MAX];
-        property_get("ro.build.fingerprint", software, "");
-        uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
+        std::string software = GetProperty("ro.build.fingerprint", "");
+        uint32_t count = static_cast<uint32_t>(software.size()) + 1;
         BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
-                reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
+                reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE,
+                writer);
     }
 
     if (nativeContext->hasCaptureTime()) {
@@ -1613,20 +1612,15 @@
 
     {
         // Setup unique camera model tag
-        char model[PROPERTY_VALUE_MAX];
-        property_get("ro.product.model", model, "");
+        std::string model = GetProperty("ro.product.model", "");
+        std::string manufacturer = GetProperty("ro.product.manufacturer", "");
+        std::string brand = GetProperty("ro.product.brand", "");
 
-        char manufacturer[PROPERTY_VALUE_MAX];
-        property_get("ro.product.manufacturer", manufacturer, "");
-
-        char brand[PROPERTY_VALUE_MAX];
-        property_get("ro.product.brand", brand, "");
-
-        String8 cameraModel(model);
+        String8 cameraModel(model.c_str());
         cameraModel += "-";
-        cameraModel += manufacturer;
+        cameraModel += manufacturer.c_str();
         cameraModel += "-";
-        cameraModel += brand;
+        cameraModel += brand.c_str();
 
         BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
                 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 83ffeff..1f7277a 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -166,7 +166,7 @@
                                jobject recycle, jintArray recycleBreaks,
                                jfloatArray recycleWidths, jfloatArray recycleAscents,
                                jfloatArray recycleDescents, jintArray recycleFlags,
-                               jint recycleLength) {
+                               jint recycleLength, jfloatArray charWidths) {
     minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
 
     size_t nBreaks = b->computeBreaks();
@@ -175,6 +175,8 @@
             recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(),
             b->getDescents(), b->getFlags());
 
+    env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths());
+
     b->finish();
 
     return static_cast<jint>(nBreaks);
@@ -256,11 +258,6 @@
     b->addReplacement(start, end, width, langTagsString.get(), makeHyphenators(env, hyphenators));
 }
 
-static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
-    minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
-    env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
-}
-
 static const JNINativeMethod gMethods[] = {
     // TODO performance: many of these are candidates for fast jni, awaiting guidance
     {"nNewBuilder", "()J", (void*) nNewBuilder},
@@ -269,8 +266,7 @@
     {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
     {"nAddStyleRun", "(JJIIZLjava/lang/String;[J)V", (void*) nAddStyleRun},
     {"nAddReplacementRun", "(JIIFLjava/lang/String;[J)V", (void*) nAddReplacementRun},
-    {"nGetWidths", "(J[F)V", (void*) nGetWidths},
-    {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II)I",
+    {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I",
         (void*) nComputeLineBreaks}
 };
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a9b849e..8ae9ada 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -98,6 +98,18 @@
 
 // ----------------------------------------------------------------------------
 
+static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
+    return reinterpret_cast<jlong>(new SurfaceComposerClient::Transaction);
+}
+
+static void releaseTransaction(SurfaceComposerClient::Transaction* t) {
+    delete t;
+}
+
+static jlong nativeGetNativeTransactionFinalizer(JNIEnv* env, jclass clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseTransaction));
+}
+
 static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
         jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
         jint windowType, jint ownerUid) {
@@ -278,69 +290,72 @@
     }
 }
 
-static void nativeOpenTransaction(JNIEnv* env, jclass clazz) {
-    SurfaceComposerClient::openGlobalTransaction();
+static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    transaction->apply(sync);
 }
 
-
-static void nativeCloseTransaction(JNIEnv* env, jclass clazz, jboolean sync) {
-    SurfaceComposerClient::closeGlobalTransaction(sync);
+static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    transaction->setAnimationTransaction();
 }
 
-static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz) {
-    SurfaceComposerClient::setAnimationTransaction();
-}
+static void nativeSetLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jint zorder) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
-static void nativeSetLayer(JNIEnv* env, jclass clazz, jlong nativeObject, jint zorder) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setLayer(zorder);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setLayer(ctrl, zorder);
 }
 
-static void nativeSetRelativeLayer(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeSetRelativeLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jobject relativeTo, jint zorder) {
+
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<IBinder> handle = ibinderForJavaObject(env, relativeTo);
 
-    ctrl->setRelativeLayer(handle, zorder);
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setRelativeLayer(ctrl, handle, zorder);
+    }
 }
 
-static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat x, jfloat y) {
+static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jfloat x, jfloat y) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setPosition(x, y);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setPosition(ctrl, x, y);
 }
 
 static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz,
+jlong transactionObj,
         jlong nativeObject) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setGeometryAppliesWithResize();
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setGeometryAppliesWithResize(ctrl);
 }
 
-static void nativeSetSize(JNIEnv* env, jclass clazz, jlong nativeObject, jint w, jint h) {
+static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jint w, jint h) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setSize(w, h);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setSize(ctrl, w, h);
 }
 
-static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong nativeObject, jint flags, jint mask) {
+static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jint flags, jint mask) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setFlags(flags, mask);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setFlags(ctrl, flags, mask);
 }
 
-static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong nativeObject, jobject regionObj) {
+static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
     if (!region) {
@@ -359,65 +374,65 @@
         }
     }
 
-    status_t err = ctrl->setTransparentRegionHint(reg);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setTransparentRegionHint(ctrl, reg);
     }
 }
 
-static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat alpha) {
+static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jfloat alpha) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setAlpha(alpha);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setAlpha(ctrl, alpha);
 }
 
-static void nativeSetColor(JNIEnv* env, jclass clazz, jlong nativeObject, jfloatArray fColor) {
+static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jfloatArray fColor) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
     float* floatColors = env->GetFloatArrayElements(fColor, 0);
     half3 color(floatColors[0], floatColors[1], floatColors[2]);
-    status_t err = ctrl->setColor(color);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setColor(ctrl, color);
 }
 
-static void nativeSetMatrix(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeSetMatrix(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jfloat dsdx, jfloat dtdx, jfloat dtdy, jfloat dsdy) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setMatrix(dsdx, dtdx, dtdy, dsdy);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setMatrix(ctrl, dsdx, dtdx, dtdy, dsdy);
 }
 
-static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jint l, jint t, jint r, jint b) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     Rect crop(l, t, r, b);
-    status_t err = ctrl->setCrop(crop);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setCrop(ctrl, crop);
 }
 
-static void nativeSetFinalCrop(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeSetFinalCrop(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jint l, jint t, jint r, jint b) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     Rect crop(l, t, r, b);
-    status_t err = ctrl->setFinalCrop(crop);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setFinalCrop(ctrl, crop);
 }
 
-static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong nativeObject, jint layerStack) {
+static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jint layerStack) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    status_t err = ctrl->setLayerStack(layerStack);
-    if (err < 0 && err != NO_INIT) {
-        doThrowIAE(env);
-    }
+    transaction->setLayerStack(ctrl, layerStack);
 }
 
 static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) {
@@ -440,6 +455,7 @@
 }
 
 static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz,
+        jlong transactionObj,
         jobject tokenObj, jlong nativeSurfaceObject) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
@@ -448,8 +464,14 @@
     if (sur != NULL) {
         bufferProducer = sur->getIGraphicBufferProducer();
     }
-    status_t err = SurfaceComposerClient::setDisplaySurface(token,
-            bufferProducer);
+
+
+    status_t err = NO_ERROR;
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        err = transaction->setDisplaySurface(token,
+                bufferProducer);
+    }
     if (err != NO_ERROR) {
         doThrowIAE(env, "Illegal Surface, could not enable async mode. Was this"
                 " Surface created with singleBufferMode?");
@@ -457,14 +479,20 @@
 }
 
 static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
+        jlong transactionObj,
         jobject tokenObj, jint layerStack) {
+
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
 
-    SurfaceComposerClient::setDisplayLayerStack(token, layerStack);
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setDisplayLayerStack(token, layerStack);
+    }
 }
 
 static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
+        jlong transactionObj,
         jobject tokenObj, jint orientation,
         jint layerStackRect_left, jint layerStackRect_top, jint layerStackRect_right, jint layerStackRect_bottom,
         jint displayRect_left, jint displayRect_top, jint displayRect_right, jint displayRect_bottom) {
@@ -472,14 +500,23 @@
     if (token == NULL) return;
     Rect layerStackRect(layerStackRect_left, layerStackRect_top, layerStackRect_right, layerStackRect_bottom);
     Rect displayRect(displayRect_left, displayRect_top, displayRect_right, displayRect_bottom);
-    SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect);
+
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setDisplayProjection(token, orientation, layerStackRect, displayRect);
+    }
 }
 
 static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
+        jlong transactionObj,
         jobject tokenObj, jint width, jint height) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
-    SurfaceComposerClient::setDisplaySize(token, width, height);
+
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setDisplaySize(token, width, height);
+    }
 }
 
 static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz,
@@ -722,52 +759,73 @@
     return JNI_TRUE;
 }
 
-static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jobject handleObject, jlong frameNumber) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<IBinder> handle = ibinderForJavaObject(env, handleObject);
 
-    ctrl->deferTransactionUntil(handle, frameNumber);
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->deferTransactionUntil(ctrl, handle, frameNumber);
+    }
 }
 
-static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jlong surfaceObject, jlong frameNumber) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
 
-    ctrl->deferTransactionUntil(barrier, frameNumber);
+    transaction->deferTransactionUntil(ctrl, barrier, frameNumber);
 }
 
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jobject newParentObject) {
+
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<IBinder> handle = ibinderForJavaObject(env, newParentObject);
 
-    ctrl->reparentChildren(handle);
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->reparentChildren(ctrl, handle);
+    }
 }
 
-static void nativeReparent(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jobject newParentObject) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<IBinder> parentHandle = ibinderForJavaObject(env, newParentObject);
-    ctrl->reparent(parentHandle);
+
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->reparent(ctrl, parentHandle);
+    }
 }
 
-static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) {
+static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    ctrl->detachChildren();
+    transaction->detachChildren(ctrl);
 }
 
-static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject,
+static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject,
         jint scalingMode) {
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
-    ctrl->setOverrideScalingMode(scalingMode);
+    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    transaction->setOverrideScalingMode(ctrl, scalingMode);
 }
 
 static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-
     return javaObjectForIBinder(env, ctrl->getHandle());
 }
 
@@ -802,37 +860,39 @@
             (void*)nativeScreenshotBitmap },
     {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
             (void*)nativeScreenshot },
-    {"nativeOpenTransaction", "()V",
-            (void*)nativeOpenTransaction },
-    {"nativeCloseTransaction", "(Z)V",
-            (void*)nativeCloseTransaction },
-    {"nativeSetAnimationTransaction", "()V",
+    {"nativeCreateTransaction", "()J",
+            (void*)nativeCreateTransaction },
+    {"nativeApplyTransaction", "(JZ)V",
+            (void*)nativeApplyTransaction },
+    {"nativeGetNativeTransactionFinalizer", "()J",
+            (void*)nativeGetNativeTransactionFinalizer },
+    {"nativeSetAnimationTransaction", "(J)V",
             (void*)nativeSetAnimationTransaction },
-    {"nativeSetLayer", "(JI)V",
+    {"nativeSetLayer", "(JJI)V",
             (void*)nativeSetLayer },
-    {"nativeSetRelativeLayer", "(JLandroid/os/IBinder;I)V",
+    {"nativeSetRelativeLayer", "(JJLandroid/os/IBinder;I)V",
             (void*)nativeSetRelativeLayer },
-    {"nativeSetPosition", "(JFF)V",
+    {"nativeSetPosition", "(JJFF)V",
             (void*)nativeSetPosition },
-    {"nativeSetGeometryAppliesWithResize", "(J)V",
+    {"nativeSetGeometryAppliesWithResize", "(JJ)V",
             (void*)nativeSetGeometryAppliesWithResize },
-    {"nativeSetSize", "(JII)V",
+    {"nativeSetSize", "(JJII)V",
             (void*)nativeSetSize },
-    {"nativeSetTransparentRegionHint", "(JLandroid/graphics/Region;)V",
+    {"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V",
             (void*)nativeSetTransparentRegionHint },
-    {"nativeSetAlpha", "(JF)V",
+    {"nativeSetAlpha", "(JJF)V",
             (void*)nativeSetAlpha },
-    {"nativeSetColor", "(J[F)V",
+    {"nativeSetColor", "(JJ[F)V",
             (void*)nativeSetColor },
-    {"nativeSetMatrix", "(JFFFF)V",
+    {"nativeSetMatrix", "(JJFFFF)V",
             (void*)nativeSetMatrix },
-    {"nativeSetFlags", "(JII)V",
+    {"nativeSetFlags", "(JJII)V",
             (void*)nativeSetFlags },
-    {"nativeSetWindowCrop", "(JIIII)V",
+    {"nativeSetWindowCrop", "(JJIIII)V",
             (void*)nativeSetWindowCrop },
-    {"nativeSetFinalCrop", "(JIIII)V",
+    {"nativeSetFinalCrop", "(JJIIII)V",
             (void*)nativeSetFinalCrop },
-    {"nativeSetLayerStack", "(JI)V",
+    {"nativeSetLayerStack", "(JJI)V",
             (void*)nativeSetLayerStack },
     {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
             (void*)nativeGetBuiltInDisplay },
@@ -840,13 +900,13 @@
             (void*)nativeCreateDisplay },
     {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
             (void*)nativeDestroyDisplay },
-    {"nativeSetDisplaySurface", "(Landroid/os/IBinder;J)V",
+    {"nativeSetDisplaySurface", "(JLandroid/os/IBinder;J)V",
             (void*)nativeSetDisplaySurface },
-    {"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V",
+    {"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
             (void*)nativeSetDisplayLayerStack },
-    {"nativeSetDisplayProjection", "(Landroid/os/IBinder;IIIIIIIII)V",
+    {"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V",
             (void*)nativeSetDisplayProjection },
-    {"nativeSetDisplaySize", "(Landroid/os/IBinder;II)V",
+    {"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
             (void*)nativeSetDisplaySize },
     {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;",
             (void*)nativeGetDisplayConfigs },
@@ -872,17 +932,17 @@
             (void*)nativeGetAnimationFrameStats },
     {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
             (void*)nativeSetDisplayPowerMode },
-    {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
+    {"nativeDeferTransactionUntil", "(JJLandroid/os/IBinder;J)V",
             (void*)nativeDeferTransactionUntil },
-    {"nativeDeferTransactionUntilSurface", "(JJJ)V",
+    {"nativeDeferTransactionUntilSurface", "(JJJJ)V",
             (void*)nativeDeferTransactionUntilSurface },
-    {"nativeReparentChildren", "(JLandroid/os/IBinder;)V",
+    {"nativeReparentChildren", "(JJLandroid/os/IBinder;)V",
             (void*)nativeReparentChildren } ,
-    {"nativeReparent", "(JLandroid/os/IBinder;)V",
+    {"nativeReparent", "(JJLandroid/os/IBinder;)V",
             (void*)nativeReparent },
-    {"nativeSeverChildren", "(J)V",
+    {"nativeSeverChildren", "(JJ)V",
             (void*)nativeSeverChildren } ,
-    {"nativeSetOverrideScalingMode", "(JI)V",
+    {"nativeSetOverrideScalingMode", "(JJI)V",
             (void*)nativeSetOverrideScalingMode },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
             (void*)nativeGetHandle },
diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h
index 1101b83..3a05195 100644
--- a/core/jni/eventlog_helper.h
+++ b/core/jni/eventlog_helper.h
@@ -17,6 +17,8 @@
 #ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
 #define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
 
+#include <memory>
+
 #include <fcntl.h>
 
 #include <android-base/macros.h>
@@ -26,6 +28,8 @@
 
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "core_jni_helpers.h"
 #include "jni.h"
 
@@ -91,20 +95,14 @@
         android_log_event_list ctx(tag);
         // Don't throw NPE -- I feel like it's sort of mean for a logging function
         // to be all crashy if you pass in NULL -- but make the NULL value explicit.
-        if (value != NULL) {
-            const char *str = env->GetStringUTFChars(value, NULL);
-            ctx << str;
-            env->ReleaseStringUTFChars(value, str);
-        } else {
-            ctx << "NULL";
-        }
+        ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL");
         return ctx.write(LogID);
     }
     static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
             jobjectArray value) {
         android_log_event_list ctx(tag);
 
-        if (value == NULL) {
+        if (value == nullptr) {
             ctx << "[NULL]";
             return ctx.write(LogID);
         }
@@ -112,26 +110,23 @@
         jsize copied = 0, num = env->GetArrayLength(value);
         for (; copied < num && copied < 255; ++copied) {
             if (ctx.status()) break;
-            jobject item = env->GetObjectArrayElement(value, copied);
-            if (item == NULL) {
+            ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied));
+            if (item == nullptr) {
                 ctx << "NULL";
-            } else if (env->IsInstanceOf(item, gStringClass)) {
-                const char *str = env->GetStringUTFChars((jstring) item, NULL);
-                ctx << str;
-                env->ReleaseStringUTFChars((jstring) item, str);
-            } else if (env->IsInstanceOf(item, gIntegerClass)) {
-                ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
-            } else if (env->IsInstanceOf(item, gLongClass)) {
-                ctx << (int64_t)env->GetLongField(item, gLongValueID);
-            } else if (env->IsInstanceOf(item, gFloatClass)) {
-                ctx << (float)env->GetFloatField(item, gFloatValueID);
+            } else if (env->IsInstanceOf(item.get(), gStringClass)) {
+                ctx << ScopedUtfChars(env, (jstring) item.get()).c_str();
+            } else if (env->IsInstanceOf(item.get(), gIntegerClass)) {
+                ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID);
+            } else if (env->IsInstanceOf(item.get(), gLongClass)) {
+                ctx << (int64_t)env->GetLongField(item.get(), gLongValueID);
+            } else if (env->IsInstanceOf(item.get(), gFloatClass)) {
+                ctx << (float)env->GetFloatField(item.get(), gFloatValueID);
             } else {
                 jniThrowException(env,
                         "java/lang/IllegalArgumentException",
                         "Invalid payload item type");
                 return -1;
             }
-            env->DeleteLocalRef(item);
         }
         return ctx.write(LogID);
     }
@@ -140,39 +135,37 @@
         readEvents(env, loggerMode, nullptr, startTime, out);
     }
 
-    static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime,
+    static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime,
             jobject out) {
-        struct logger_list *logger_list;
+        std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list(
+                nullptr, android_logger_list_close);
         if (startTime) {
-            logger_list = android_logger_list_alloc_time(loggerMode,
-                    log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
+            logger_list.reset(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);
+            logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0));
         }
         if (!logger_list) {
             jniThrowIOException(env, errno);
             return;
         }
 
-        if (!android_logger_open(logger_list, LogID)) {
+        if (!android_logger_open(logger_list.get(), LogID)) {
             jniThrowIOException(env, errno);
-            android_logger_list_free(logger_list);
             return;
         }
 
-        jsize tagLength = 0;
-        jint *tagValues = nullptr;
-        if (tags != nullptr) {
-            tagLength = env->GetArrayLength(tags);
-            tagValues = env->GetIntArrayElements(tags, NULL);
+        ScopedIntArrayRO tags(env);
+        if (jTags != nullptr) {
+            tags.reset(jTags);
         }
 
         while (1) {
             log_msg log_msg;
-            int ret = android_logger_list_read(logger_list, &log_msg);
+            int ret = android_logger_list_read(logger_list.get(), &log_msg);
 
             if (ret == 0) {
-                break;
+                return;
             }
             if (ret < 0) {
                 if (ret == -EINTR) {
@@ -183,7 +176,7 @@
                 } else if (ret != -EAGAIN) {
                     jniThrowIOException(env, -ret);  // Will throw on return
                 }
-                break;
+                return;
             }
 
             if (log_msg.id() != LogID) {
@@ -192,10 +185,10 @@
 
             int32_t tag = * (int32_t *) log_msg.msg();
 
-            if (tags != nullptr) {
+            if (jTags != nullptr) {
                 bool found = false;
-                for (int i = 0; !found && i < tagLength; ++i) {
-                    found = (tag == tagValues[i]);
+                for (size_t i = 0; !found && i < tags.size(); ++i) {
+                    found = (tag == tags[i]);
                 }
                 if (!found) {
                     continue;
@@ -203,33 +196,27 @@
             }
 
             jsize len = ret;
-            jbyteArray array = env->NewByteArray(len);
-            if (array == NULL) {
-                break;
+            ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len));
+            if (array == nullptr) {
+                return;
             }
 
-            jbyte *bytes = env->GetByteArrayElements(array, NULL);
-            memcpy(bytes, log_msg.buf, len);
-            env->ReleaseByteArrayElements(array, bytes, 0);
-
-            jobject event = env->NewObject(gEventClass, gEventInitID, array);
-            if (event == NULL) {
-                break;
+            {
+                ScopedByteArrayRW bytes(env, array.get());
+                memcpy(bytes.get(), log_msg.buf, len);
             }
 
-            env->CallBooleanMethod(out, gCollectionAddID, event);
-            env->DeleteLocalRef(event);
-            env->DeleteLocalRef(array);
+            ScopedLocalRef<jobject> event(env,
+                    env->NewObject(gEventClass, gEventInitID, array.get()));
+            if (event == nullptr) {
+                return;
+            }
+
+            env->CallBooleanMethod(out, gCollectionAddID, event.get());
             if (env->ExceptionCheck() == JNI_TRUE) {
-                break;
+                return;
             }
         }
-
-        android_logger_list_close(logger_list);
-
-        if (tags != nullptr) {
-            env->ReleaseIntArrayElements(tags, tagValues, 0);
-        }
     }
 
 private:
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 86e31d0..8d85038 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -20,6 +20,8 @@
 
 package android.os;
 
+import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
+
 message BatteryStatsProto {
   int32 report_version = 1;
   int64 parcel_version = 2;
@@ -55,6 +57,383 @@
 }
 
 message SystemProto {
+  message Battery {
+    // Wall clock time when the data collection started.
+    // In case of device time manually reset by users:
+    //   start_clock_time_ms keeps the same value in the current collection
+    //   period and changes for later collection periods.
+    int64 start_clock_time_ms = 1;
+    // #times the device has been started since start_clock_time_millis.
+    int64 start_count = 2;
+    // Total realtime duration (= SINCE_UNPLUGGED battery_realtime_millis.)
+    int64 total_realtime_ms = 3;
+    int64 total_uptime_ms = 4;
+    // Realtime duration on battery.
+    int64 battery_realtime_ms = 5;
+    // Uptime duration (i.e., not suspend).
+    // Uptime is anytime the CPUs were on. The radio and Wifi chip
+    // can be running while the CPUs are off.
+    int64 battery_uptime_ms = 6;
+    // Total realtime duration measured with screen off or dozing.
+    int64 screen_off_realtime_ms = 7;
+    // Total uptime duration measured with screen off or dozing.
+    int64 screen_off_uptime_ms = 8;
+    // Total time the screen was dozing while the device was running on battery.
+    // For historical reasons, screen_doze_duration_msec is a subset of
+    // screen_off_realtime_msec.
+    int64 screen_doze_duration_ms = 9;
+    // The estimated real battery capacity, which may be less than the declared
+    // battery capacity (for example, because of battery aging). This field is
+    // less reliable than min(max)_learned_battery_capacity_uah, use those two
+    // fields whenever possible.
+    int64 estimated_battery_capacity_mah = 10;
+    // The minimum learned battery capacity in uAh.
+    int64 min_learned_battery_capacity_uah = 11;
+    // The maximum learned battery capacity in uAh.
+    int64 max_learned_battery_capacity_uah = 12;
+  };
+  Battery battery = 1;
+
+  message BatteryDischarge {
+    // Discharged battery percentage points since the stats were last reset
+    // after charging (lower bound approximation).
+    int32 lower_bound_since_charge = 1;
+    // Upper bound approximation.
+    int32 upper_bound_since_charge = 2;
+    // Discharged points while screen is on.
+    int32 screen_on_since_charge = 3;
+    // Discharged points while screen is off.
+    int32 screen_off_since_charge = 4;
+    // Discharged points while screen was dozing. For historical reasons,
+    // screen_doze_since_charge is a subset of screen_off_since_charge.
+    int32 screen_doze_since_charge = 5;
+    // Total amount of battery discharged in mAh. This will only be non-zero for
+    // devices that report battery discharge via a coulomb counter.
+    int64 total_mah = 6;
+    // Total amount of battery discharged while the screen was off in mAh.
+    // This will only be non-zero for devices that report battery discharge
+    // via a coulomb counter.
+    int64 total_mah_screen_off = 7;
+    // Total amount of battery discharged while the screen was dozing in mAh.
+    // This will only be non-zero for devices that report battery discharge
+    // via a coulomb counter. For historical reasons, total_mah_screen_doze is
+    // a subset of total_mah_screen_off.
+    int64 total_mah_screen_doze = 8;
+  };
+  BatteryDischarge battery_discharge = 2;
+
+  oneof time_remaining {
+    // Approximation for how much time remains until the battery is fully
+    // charged. The device will print -1 if there wasn't enough data to
+    // calculate an estimate, or if the battery is currently discharging.
+    int64 charge_time_remaining_ms = 3;
+    // Approximation for how much time remains until the battery is fully
+    // discharged. The device will print -1 if there wasn't enough data to
+    // calculate an estimate, or if the battery is currently charging.
+    int64 discharge_time_remaining_ms = 4;
+  }
+
+  // BatteryLevelStep tracks data for which conditions were continuously held for
+  // the entire duration. Field for which the conditions were not consistent
+  // for the entire duration should be marked MIXED.
+  message BatteryLevelStep {
+    // How long the battery was at the current level.
+    int64 duration_ms = 1;
+    // Battery level
+    int32 level = 2;
+
+    // State of the display. A special enum is used rather than
+    // DisplayProto.State because a MIXED value needs to be in the enum, and
+    // batterystats doesn't care about all of the different display states.
+    enum DisplayState {
+      DS_MIXED = 0;
+      DS_ON = 1;
+      DS_OFF = 2;
+      DS_DOZE = 3;
+      DS_DOZE_SUSPEND = 4;
+      // Any display state error that comes through should be sent to hackbod@.
+      DS_ERROR = 5;
+    }
+    // The state of the display for the entire battery level step. MIXED is used
+    // if there were multiple states for this step.
+    DisplayState display_state = 3;
+
+    // Indicates status in power save mode.
+    enum PowerSaveMode {
+      PSM_MIXED = 0;
+      PSM_ON = 1;
+      PSM_OFF = 2;
+    }
+    // Battery Saver mode for the entire battery level step. MIXED is used
+    // if there were multiple states for this step.
+    PowerSaveMode power_save_mode = 4;
+
+    // Indicates status in idle mode.
+    enum IdleMode {
+      IM_MIXED = 0;
+      IM_ON = 2;
+      IM_OFF = 3;
+    }
+    // Doze mode for the entire battery level step. MIXED is used if there were
+    // multiple states for this step.
+    IdleMode idle_mode = 5;
+  };
+  // Battery level steps when the device was charging.
+  repeated BatteryLevelStep charge_step = 5;
+  // Battery level steps when the device was discharging.
+  repeated BatteryLevelStep discharge_step = 6;
+
+  // All CPU frequencies of the device.
+  repeated int64 cpu_frequency = 7;
+
+  message DataConnection {
+    enum Name {
+      NONE = 0;
+      GPRS = 1;
+      EDGE = 2;
+      UMTS = 3;
+      CDMA = 4;
+      EVDO_0 = 5;
+      EVDO_A = 6;
+      ONE_X_RTT = 7; // 1xRTT.
+      HSDPA = 8;
+      HSUPA = 9;
+      HSPA = 10;
+      IDEN = 11;
+      EVDO_B = 12;
+      LTE = 13;
+      EHRPD = 14;
+      HSPAP = 15;
+      OTHER = 16;
+    };
+    Name name = 1;
+    TimerProto total = 2;
+  };
+  repeated DataConnection data_connection = 8;
+
+  ControllerActivityProto global_bluetooth_controller = 9;
+  ControllerActivityProto global_modem_controller = 10;
+  ControllerActivityProto global_wifi_controller = 11;
+
+  message GlobalNetwork {
+    // Total Bytes received on mobile connections.
+    int64 mobile_bytes_rx = 1;
+    // Total Bytes transmitted on mobile connections.
+    int64 mobile_bytes_tx = 2;
+    // Total Bytes received on wifi connections.
+    int64 wifi_bytes_rx = 3;
+    // Total Bytes transmitted on wifi connections.
+    int64 wifi_bytes_tx = 4;
+    // Total Packets received on mobile connections.
+    int64 mobile_packets_rx = 5;
+    // Total Packets transmitted on mobile connections.
+    int64 mobile_packets_tx = 6;
+    // Total Packets received on wifi connections.
+    int64 wifi_packets_rx = 7;
+    // Total Packets transmitted on wifi connections.
+    int64 wifi_packets_tx = 8;
+    // Total Bytes received on bluetooth connections.
+    int64 bt_bytes_rx = 9;
+    // Total Bytes transmitted on bluetooth connections.
+    int64 bt_bytes_tx = 10;
+  };
+  GlobalNetwork global_network = 12;
+
+  message GlobalWifi {
+    // The amount of time that wifi has been on while the device was running on
+    // battery.
+    int64 on_duration_ms = 1;
+    // The amount of time that wifi has been on and the driver has been in the
+    // running state while the device was running on battery.
+    int64 running_duration_ms = 2;
+  }
+  GlobalWifi global_wifi = 13;
+
+  // Kernel wakelock metrics are only recorded when the device is unplugged
+  // *and* the screen is off.
+  message KernelWakelock {
+    string name = 1;
+    // Kernel wakelock stats aren't apportioned across all kernel wakelocks (as
+    // app wakelocks stats are).
+    TimerProto total = 2;
+    // The kernel doesn't have the data to enable printing out current and max
+    // durations.
+  };
+  repeated KernelWakelock kernel_wakelock = 14;
+
+  message Misc {
+    int64 screen_on_duration_ms = 1;
+    int64 phone_on_duration_ms = 2;
+    int64 full_wakelock_total_duration_ms = 3;
+    // The total elapsed time that a partial wakelock was held. This duration
+    // does not double count wakelocks held at the same time.
+    int64 partial_wakelock_total_duration_ms = 4;
+    int64 mobile_radio_active_duration_ms = 5;
+    // The time that is the difference between the mobile radio time we saw
+    // based on the elapsed timestamp when going down vs. the given time stamp
+    // from the radio.
+    int64 mobile_radio_active_adjusted_time_ms = 6;
+    int32 mobile_radio_active_count = 7;
+    // The amount of time that the mobile network has been active (in a high
+    // power state) but not being able to blame on an app.
+    int32 mobile_radio_active_unknown_duration_ms = 8;
+    // Total amount of time the device was in the interactive state.
+    int64 interactive_duration_ms = 9;
+    int64 battery_saver_mode_enabled_duration_ms = 10;
+    int32 num_connectivity_changes = 11;
+    // Amount of time the device was in deep Doze.
+    int64 deep_doze_enabled_duration_ms = 12;
+    // How many times the device went into deep Doze mode.
+    int32 deep_doze_count = 13;
+    // Amount of time the device was idling in deep Doze. Idling time
+    // encompasses "doze" time and the maintenance windows that allow apps to
+    // operate.
+    int64 deep_doze_idling_duration_ms = 14;
+    // How many times the device idling for deep Doze mode.
+    int32 deep_doze_idling_count = 15;
+    int64 longest_deep_doze_duration_ms = 16;
+    // Amount of time the device was in Doze Light.
+    int64 light_doze_enabled_duration_ms = 17;
+    // How many times the device went into Doze Light mode.
+    int32 light_doze_count = 18;
+    // Amount of time the device was idling in Doze Light. Idling time
+    // encompasses "doze" time and the maintenance windows that allow apps to
+    // operate.
+    int64 light_doze_idling_duration_ms = 19;
+    // How many times the device idling for Doze Light mode.
+    int32 light_doze_idling_count = 20;
+    int64 longest_light_doze_duration_ms = 21;
+  }
+  Misc misc = 15;
+
+  message PhoneSignalStrength {
+    android.telephony.SignalStrengthProto.StrengthName name = 1;
+    TimerProto total = 2;
+  };
+  repeated PhoneSignalStrength phone_signal_strength = 16;
+
+  message PowerUseItem {
+    enum Sipper {
+      UNKNOWN_SIPPER = 0;
+      IDLE = 1;
+      CELL = 2;
+      PHONE = 3;
+      WIFI = 4;
+      BLUETOOTH = 5;
+      FLASHLIGHT = 6;
+      SCREEN = 7;
+      USER = 8;
+      UNACCOUNTED = 9;
+      OVERCOUNTED = 10;
+      CAMERA = 11;
+      MEMORY = 12;
+    };
+    Sipper name = 1;
+    // UID, only valid for the USER sipper.
+    int32 uid = 2;
+    // Estimated power use in mAh.
+    double computed_power_mah = 3;
+    // Starting in Oreo, Battery Settings has two modes to display the battery
+    // info. The first is "app usage list". In this mode, items with should_hide
+    // enabled are hidden.
+    bool should_hide = 4;
+    // Smeared power from screen usage. Screen usage power is split and smeared
+    // among apps, based on activity time.
+    double screen_power_mah = 5;
+    // Smeared power using proportional method. Power usage from hidden sippers
+    // is smeared to all apps proportionally (except for screen usage).
+    double proportional_smear_mah = 6;
+  };
+  repeated PowerUseItem power_use_item = 17;
+
+  message PowerUseSummary {
+    double battery_capacity_mah = 1;
+    double computed_power_mah = 2;
+    // Lower bound of actual power drained.
+    double min_drained_power_mah = 3;
+    // Upper bound of actual power drained.
+    double max_drained_power_mah = 4;
+  };
+  PowerUseSummary power_use_summary = 18;
+
+  message ResourcePowerManager {
+    string name = 1;
+    TimerProto total = 2;
+    TimerProto screen_off = 3;
+  }
+  ResourcePowerManager resource_power_manager = 19;
+
+  message ScreenBrightness {
+    enum Name {
+      DARK = 0; // Not screen-off.
+      DIM = 1;
+      MEDIUM = 2;
+      LIGHT = 3;
+      BRIGHT = 4;
+    };
+    Name name = 1;
+    TimerProto total = 2;
+  };
+  repeated ScreenBrightness screen_brightness = 20;
+
+  // Duration and number of times trying to acquire a signal
+  TimerProto signal_scanning = 21;
+
+  message WakeupReason {
+    string name = 1;
+    TimerProto total = 2;
+  };
+  repeated WakeupReason wakeup_reason = 22;
+
+  message WifiSignalStrength {
+    enum Name {
+      NONE = 0;
+      POOR = 1;
+      MODERATE = 2;
+      GOOD = 3;
+      GREAT = 4;
+    };
+    Name name = 1;
+    TimerProto total = 2;
+  };
+  repeated WifiSignalStrength wifi_signal_strength = 23;
+
+  message WifiState {
+    enum Name {
+      OFF = 0;
+      OFF_SCANNING = 1;
+      ON_NO_NETWORKS = 2;
+      ON_DISCONNECTED = 3;
+      ON_CONNECTED_STA = 4;
+      ON_CONNECTED_P2P = 5;
+      ON_CONNECTED_STA_P2P = 6;
+      SOFT_AP = 7;
+    };
+    Name name = 1;
+    TimerProto total = 2;
+  };
+  repeated WifiState wifi_state = 24;
+
+  message WifiSupplicantState {
+    enum Name {
+      INVALID = 0;
+      DISCONNECTED = 1;
+      INTERFACE_DISABLED = 2;
+      INACTIVE = 3;
+      SCANNING = 4;
+      AUTHENTICATING = 5;
+      ASSOCIATING = 6;
+      ASSOCIATED = 7;
+      FOUR_WAY_HANDSHAKE = 8;
+      GROUP_HANDSHAKE = 9;
+      COMPLETED = 10;
+      DORMANT = 11;
+      UNINITIALIZED = 12;
+    };
+    Name name = 1;
+    TimerProto total = 2;
+  };
+  repeated WifiSupplicantState wifi_supplicant_state = 25;
 }
 
 message TimerProto {
diff --git a/cmds/statsd/src/parse_util.h b/core/proto/android/telephony/signalstrength.proto
similarity index 60%
copy from cmds/statsd/src/parse_util.h
copy to core/proto/android/telephony/signalstrength.proto
index 8b82e7b..ff230cb 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/core/proto/android/telephony/signalstrength.proto
@@ -13,24 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
 
-#include "DropboxWriter.h"
-#include "LogReader.h"
+syntax = "proto3";
 
-#include <log/logprint.h>
+option java_package = "android.telephony";
+option java_multiple_files = true;
 
-namespace android {
-namespace os {
-namespace statsd {
+package android.telephony;
 
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-
-#endif  // PARSE_UTIL_H
+/**
+ * An android.telephony.SignalStrength object.
+ */
+message SignalStrengthProto {
+  enum StrengthName {
+    SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+    SIGNAL_STRENGTH_POOR = 1;
+    SIGNAL_STRENGTH_MODERATE = 2;
+    SIGNAL_STRENGTH_GOOD = 3;
+    SIGNAL_STRENGTH_GREAT = 4;
+  }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ff579a6..3d5ae3d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -551,6 +551,9 @@
     <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
     <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
 
+    <!-- Made protected in P (was introduced in JB-MR2) -->
+    <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 5d6f04f..8bc50b5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Werkprofiel is weens ontbrekende administrasieprogram uitgevee"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Die werkprofiel se administrasieprogram ontbreek of is korrup. Gevolglik is jou werkprofiel en verwante data uitgevee. Kontak jou administrateur vir bystand."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jou werkprofiel is nie meer op hierdie toestel beskikbaar nie"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Te veel wagwoordpogings"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Toestel word bestuur"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Jou organisasie bestuur hierdie toestel en kan netwerkverkeer monitor. Tik vir besonderhede."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Jou toestel sal uitgevee word"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM word nie toegelaat nie"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Foon word nie toegelaat nie"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Opspringvenster"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ffae89c..77a0ed7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"ሲም አይፈቀድም"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ስልክ አይፈቀድም"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ብቅ-ባይ መስኮት"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4b20e74..7b516c5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -180,8 +180,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"تم حذف الملف الشخصي للعمل نتيجة فقد تطبيق المشرف"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"تطبيق المشرف للملف الشخصي للعمل مفقود أو تالف لذا تم حذف الملف الشخصي للعمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"تتم إدارة الجهاز"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"تدير مؤسستك هذا الجهاز ويمكنها مراقبة حركة بيانات الشبكة. يمكنك النقر للحصول على تفاصيل."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"سيتم محو بيانات جهازك."</string>
@@ -1930,4 +1929,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"‏غير مسموح باستخدام SIM"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"غير مسموح باستخدام الهاتف"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"نافذة منبثقة"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 7ff8d04..8ff588b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-ə icazə verilmir"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefona icazə verilmir"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Popap Pəncərəsi"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <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 bc1e5ab..b1e1117 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -174,8 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil za Work je izbrisan jer nedostaje aplikacija za administratore"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacija za administratore na profilu za Work nedostaje ili je oštećena. Zbog toga su profil za Work i povezani podaci izbrisani. Obratite se administratoru za pomoć."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil za Work više nije dostupan na ovom uređaju"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše pokušaja unosa lozinke"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti obrisan"</string>
@@ -1825,4 +1824,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kartica nije dozvoljena"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dozvoljen"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Iskačući prozor"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index b643078..42937df 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Працоўны профіль выдалены з-за адсутнасці праграмы адміністратара"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Праграма адміністратара для працоўнага профілю адсутнічае або пашкоджана. У выніку гэтага ваш працоўны профіль і звязаныя з ім даныя былі выдалены. Звярніцеся па дапамогу да адміністратара."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш працоўны профіль больш не даступны на гэтай прыладзе"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Занадта шмат спроб уводу пароля"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Прылада знаходзіцца пад кіраваннем"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Даныя вашай прылады будуць сцерты"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-карта не дапускаецца"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Тэлефон не дапускаецца"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Выплыўное акно"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 44df773..5643b7d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картата не е разрешена"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонът не е разрешен"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Изскачащ прозорец"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 76a6b0e..48b8e64 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"প্রশাসক অ্যাপ না থাকায় কর্মস্থলের প্রোফাইল মুছে ফেলা হয়েছে"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"কর্মস্থলের প্রোফাইলের প্রশাসক অ্যাপটি হয় নেই, অথবা সেটি ক্ষতিগ্রস্ত হয়েছে৷ এর ফলে আপনার কর্মস্থলের প্রোফাইল এবং সম্পর্কিত ডেটা মুছে ফেলা হয়েছে৷ সহায়তার জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন৷"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"আপনার কর্মস্থলের প্রোফাইলটি আর এই ডিভাইসে নেই"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"বহুবার ভুল পাসওয়ার্ড দিয়েছেন"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"আপনার ডিভাইসটি মুছে ফেলা হবে"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"সিম অনুমোদিত নয়"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ফোন অনুমোদিত নয়"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"পপ-আপ উইন্ডো"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 0778b0d..c4a6d69 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -174,8 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni profil je izbrisan jer nedostaje aplikacija administratora"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Nedostaje aplikacija administratora za radni profil ili je neispravna. Zbog toga su vaš radni profil i povezani podaci izbrisani. Obratite administratoru za pomoć."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Radni profil više nije dostupan na ovom uređaju"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše puta ste pokušali otključati uređaj"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja."</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti izbrisan"</string>
@@ -1827,4 +1826,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kartica nije dozvoljena"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dozvoljen"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Iskočni prozor"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d910bf7..af48cf7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"S\'ha suprimit el perfil professional perquè falta l\'aplicació d\'administració"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta l\'aplicació d\'administració del perfil professional o està malmesa. Com a conseqüència, s\'han suprimit el teu perfil professional i les dades relacionades. Contacta amb l\'administrador per obtenir ajuda."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"El teu perfil professional ja no està disponible en aquest dispositiu"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Has intentat introduir la contrasenya massa vegades"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"El dispositiu està gestionat"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"La teva organització gestiona aquest dispositiu i és possible que supervisi el trànsit de xarxa. Toca per obtenir més informació."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"El contingut del dispositiu s\'esborrarà"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no compatible"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telèfon no no compatible"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Finestra emergent"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c0760b1..d1029e6 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovní profil byl smazán, protože není k dispozici aplikace pro správu"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikace pro správu pracovního profilu chybí nebo je poškozena. Váš pracovní profil a související data proto byla smazána. Požádejte o pomoc administrátora."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovní profil v tomto zařízení již není k dispozici"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Příliš mnoho pokusů o zadání hesla"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Zařízení je spravováno"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Toto zařízení je spravováno vaší organizací, která může sledovat síťový provoz. Podrobnosti zobrazíte klepnutím."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Zařízení bude vymazáno"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta není povolena"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon není povolen"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Vyskakovací okno"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 7b941b3..66af879 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbejdsprofilen blev slettet, fordi der mangler en administrationsapp"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administrationsappen til arbejdsprofilen mangler eller er beskadiget. Derfor er din arbejdsprofil og dine relaterede data blevet slettet. Kontakt din administrator for at få hjælp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Din arbejdsprofil er ikke længere tilgængelig på denne enhed"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"For mange mislykkede adgangskodeforsøg"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dette er en administreret enhed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se oplysninger."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheden slettes"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortet har ikke adgangstilladelse"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonen har ikke adgangstilladelse"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop op-vindue"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 68b0355..f76408a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbeitsprofil aufgrund fehlender Admin-App gelöscht"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Die Admin-App für das Arbeitsprofil fehlt oder ist beschädigt. Daher wurden dein Arbeitsprofil und alle zugehörigen Daten gelöscht. Bitte wende dich für weitere Hilfe an deinen Administrator."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Dein Arbeitsprofil ist auf diesem Gerät nicht mehr verfügbar"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Zu viele falsche Passworteingaben"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dies ist ein verwaltetes Gerät"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Deine Organisation verwaltet dieses Gerät und überprüft unter Umständen den Netzwerkverkehr. Tippe hier, um weitere Informationen zu erhalten."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Die Daten auf deinem Gerät werden gelöscht."</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-Karte nicht zulässig"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone nicht zulässig"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-up-Fenster"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 9ca0a86..d2cc930 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Το προφίλ εργασίας διαγράφηκε λόγω απουσίας της εφαρμογής διαχείρισης"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Η εφαρμογή διαχείρισης προφίλ εργασίας είτε λείπει είτε είναι κατεστραμμένη. Ως αποτέλεσμα, διαγράφηκε το προφίλ εργασίας και τα σχετικά δεδομένα. Επικοινωνήστε με τον διαχειριστή σας για βοήθεια."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Το προφίλ εργασίας σας δεν είναι πια διαθέσιμο σε αυτήν τη συσκευή"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Πάρα πολλές προσπάθειες εισαγωγής κωδικού πρόσβασης"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Η συσκευή είναι διαχειριζόμενη"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ο οργανισμός σας διαχειρίζεται αυτήν τη συσκευή και ενδέχεται να παρακολουθεί την επισκεψιμότητα δικτύου. Πατήστε για λεπτομέρειες."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Η συσκευή σας θα διαγραφεί"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Η κάρτα SIM δεν επιτρέπεται"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Το τηλέφωνο δεν επιτρέπεται"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Αναδυόμενο παράθυρο"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 73e55da..ad03374 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 73e55da..ad03374 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 73e55da..ad03374 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 73e55da..ad03374 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index db7ab4f..d6da98d 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎SIM not allowed‎‏‎‎‏‎"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎Phone not allowed‎‏‎‎‏‎"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎Popup Window‎‏‎‎‏‎"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 16a43ba..ea8b54e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no permitida"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Teléfono no permitido"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Ventana emergente"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5827a97..6164bac 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no compatible"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Teléfono no compatible"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Ventana emergente"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1d40f4a..b48afe0 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kaart pole lubatud"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon pole lubatud"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Hüpikaken"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a864f94..98b5f5a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Ez da onartzen SIM txartela"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Ez da onartzen telefonoa"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Leiho gainerakorra"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 77336ff..9385f58 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"به دلیل نداشتن برنامه سرپرست، نمایه کاری حذف شد"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"برنامه سرپرست نمایه کاری یا وجود ندارد یا خراب است. در نتیجه، نمایه کاری شما و داده‌های مرتبط با آن حذف شده است. برای دریافت راهنمایی با سرپرست سیستم تماس بگیرید."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"نمایه کاری شما دیگر در این دستگاه دردسترس نیست"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"تلاش‌های بسیار زیادی برای وارد کردن گذرواژه انجام شده است"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"دستگاه مدیریت می‌شود"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"سازمانتان این دستگاه را مدیریت می‌کند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، ضربه بزنید."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"دستگاهتان پاک خواهد شد"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"سیم‌کارت مجاز نیست"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"تلفن مجاز نیست"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"پنجره بازشو"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0c4e883..c523452 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortti estetty"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Puhelin estetty"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Ponnahdusikkuna"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a8ab142..4f6b3aa 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil professionnel supprimé en raison de l\'application d\'administration manquante"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Le profil professionnel de l\'application d\'administration est manquant ou corrompu. Votre profil professionnel et ses données connexes ont donc été supprimés. Communiquez avec votre administrateur pour obtenir de l\'assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Votre profil professionnel n\'est plus accessible sur cet appareil"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Trop de tentatives d\'entrée du mot de passe"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"L\'appareil est géré"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Le contenu de votre appareil sera effacé"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Carte SIM non autorisée"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Téléphone non autorisé"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre contextuelle"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4c2bcca..92838e4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Carte SIM non autorisée"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Téléphone non autorisé"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 12cbbb3..6d56700 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Eliminouse o perfil de traballo porque falta a aplicación de administración"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta a aplicación de administración do perfil de traballo ou ben está danada. Como resultado, eliminouse o teu perfil de traballo e os datos relacionados. Para obter asistencia, contacta co administrador."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"O teu perfil de traballo xa non está dispoñible neste dispositivo"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiados intentos de introdución do contrasinal"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo está xestionado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"A túa organización xestiona este dispositivo e pode controlar o tráfico de rede. Toca para obter máis detalles."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Borrarase o teu dispositivo"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Non se admite a tarxeta SIM"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Non se admite o teléfono"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Ventá emerxente"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4cb735d..afc082b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ખૂટતી વ્યવસ્થાપક ઍપ્લિકેશનને કારણે કાર્યાલયની પ્રોફાઇલ કાઢી નાખી"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"કાર્ય પ્રોફાઇલ વ્યવસ્થાપક ઍપ્લિકેશન ખૂટે છે અથવા તો દૂષિત છે. પરિણામે, તમારી કાર્યાલયની પ્રોફાઇલ અને તે સંબંધિત ડેટા કાઢી નાખવામાં આવ્યો છે. સહાયતા માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"આ ઉપકરણ પર તમારી કાર્યાલયની પ્રોફાઇલ હવે ઉપલબ્ધ નથી"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"પાસવર્ડના ઘણા વધુ પ્રયત્નો"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ઉપકરણ સંચાલિત છે"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"તમારું ઉપકરણ કાઢી નાખવામાં આવશે"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"સિમ મંજૂર નથી"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ફોન મંજૂર નથી"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"પૉપઅપ વિંડો"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3e37771..925565a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"अनुपलब्ध व्यवस्थापक ऐप्लिकेशन के कारण कार्य प्रोफ़ाइल हटा दी गई"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफ़ाइल व्यवस्थापक ऐप्लिकेशन या तो मौजूद नहीं है या वह खराब हो गया है. परिणामस्वरूप, आपकी कार्य प्रोफ़ाइल और उससे जुड़े डेटा को हटा दिया गया है. सहायता के लिए अपने व्यवस्थापक से संपर्क करें."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपकी कार्य प्रोफ़ाइल अब इस डिवाइस पर उपलब्‍ध नहीं है"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"कई बार गलत पासवर्ड डाला गया"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"डिवाइस प्रबंधित है"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"आपका संगठन इस डिवाइस का प्रबंधन करता है और वह नेटवर्क ट्रैफ़िक की निगरानी भी कर सकता है. विवरण के लिए टैप करें."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"आपके डिवाइस को मिटा दिया जाएगा"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM की अनुमति नहीं है"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"फ़ोन की अनुमति नहीं है"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3c295c8..a7fa3ce 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -174,8 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni je profil izbrisan jer nedostaje administratorska aplikacija"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorska aplikacija radnog profila nedostaje ili je oštećena. Zbog toga su radni profil i povezani podaci izbrisani. Za pomoć se obratite svom administratoru."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vaš radni profil više nije dostupan na ovom uređaju"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše pokušaja unosa zaporke"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređaj je upravljan"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će se izbrisati"</string>
@@ -1825,4 +1824,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM nije dopušten"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dopušten"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Skočni prozor"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 4e8d03c..b1ef463 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"A munkaprofilt a rendszer hiányzó rendszergazdai alkalmazás miatt törölte"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"A munkaprofil rendszergazdai alkalmazása hiányzik vagy sérült. A rendszer ezért törölte a munkaprofilt, és az ahhoz kapcsolódó adatokat. Ha segítségre van szüksége, vegye fel a kapcsolatot rendszergazdájával."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Munkaprofilja már nem hozzáférhető ezen az eszközön."</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Túl sok jelszómegadási kísérlet"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Felügyelt eszköz"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ezt az eszközt szervezete kezeli, és lehetséges, hogy a hálózati forgalmat is figyelik. További részletekért koppintson."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"A rendszer törölni fogja eszközét"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"A SIM-kártya nem engedélyezett"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"A telefon nem engedélyezett"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Előugró ablak"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a5fbe34f..b367c67 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM քարտի օգտագործումն արգելված է"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Հեռախոսի օգտագործումն արգելված է"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Հայտնվող պատուհան"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 173ad40..a9d4316 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM tidak diizinkan"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Ponsel tidak diizinkan"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Jendela Pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c61c26e..3cdee4c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Vinnusniði eytt vegna þess að stjórnunarforrit vantar"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Stjórnunarforrit vinnusniðsins vantar eða er skemmt. Vinnusniðinu og gögnum því tengdu hefur því verið eytt. Hafðu samband við kerfisstjórann til að fá frekari aðstoð."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vinnusniðið þitt er ekki lengur í boði á þessu tæki"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Of margar tilraunir til að slá inn aðgangsorð"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Tækinu er stjórnað"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Fyrirtækið þitt stjórnar þessu tæki og kann að fylgjast með netnotkun. Ýttu hér til að fá upplýsingar."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Tækið verður hreinsað"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kort er ekki leyft"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Sími er ekki leyfður"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Sprettigluggi"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7e54a9c7..f08ed1b 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Scheda SIM non consentita"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefono non consentito"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Finestra popup"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 23b8e38..98b17dc 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"פרופיל העבודה נמחק מפני שחסרה אפליקציית ניהול"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"אפליקציית הניהול של פרופיל העבודה חסרה או פגומה. כתוצאה מכך, פרופיל העבודה שלך נמחק, כולל כל הנתונים הקשורים אליו. לקבלת עזרה, פנה למנהל המערכת."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"פרופיל העבודה שלך אינו זמין עוד במכשיר הזה"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"בוצעו ניסיונות רבים מדי להזנת סיסמה"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"המכשיר מנוהל"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"הארגון שלך מנהל מכשיר זה ועשוי לנטר את התנועה ברשת. הקש לקבלת פרטים."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"תתבצע מחיקה של המכשיר"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"‏כרטיס ה-SIM לא מורשה"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"הטלפון לא מורשה"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"חלון קופץ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2100470..66ce56c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"管理アプリがないため仕事用プロファイルが削除されました"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"仕事用プロファイルの管理アプリがないか、破損しています。そのため仕事用プロファイルと関連データが削除されました。管理者にサポートをご依頼ください。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"お使いの仕事用プロファイルはこの端末で使用できなくなりました"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"パスワード入力回数が上限を超えました"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"管理対象の端末"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"この端末は組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"端末のデータが消去されます"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM は許可されていません"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"電話は許可されていません"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ポップアップ ウィンドウ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ee8daad..879270b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM ბარათი დაუშვებელია"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ტელეფონი დაუშვებელია"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ამომხტარი ფანჯარა"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index bbf1b4a9..36dc3a6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Әкімші қолданбасы болмағандықтан жұмыс профилі жойылды"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Жұмыс профилінің әкімші қолданбасы жоқ немесе бүлінген. Нәтижесінде жұмыс профиліңіз және қатысты деректер жойылды. Көмек алу үшін әкімшіге хабарласыңыз."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жұмыс профиліңіз осы құрылғыда енді қолжетімді емес"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Тым көп құпия сөз енгізу әрекеті жасалды"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Құрылғы басқарылады"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін. Мәліметтер алу үшін түртіңіз."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Құрылғыңыздағы деректер өшіріледі"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картасына рұқсат етілмеген"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонға рұқсат етілмеген"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Қалқымалы терезе"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 128c3f3..b8f9f8b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"កម្រងព័ត៌មាន​ការងារ​ត្រូវបាន​លុប​ដោយសារ​បាត់​កម្មវិធី​អ្នកគ្រប់គ្រង"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"កម្មវិធី​អ្នកគ្រប់គ្រង​កម្រងព័ត៌មាន​ការងារនេះ​អាច​បាត់ ឬ​មាន​បញ្ហា។ ដូច្នេះហើយ​ទើប​កម្រងព័ត៌មាន​ការងារ​របស់អ្នក និង​ទិន្នន័យ​ដែល​ពាក់ព័ន្ធត្រូវ​បានលុប។ សូមទាក់ទង​ទៅអ្នក​គ្រប់គ្រង​របស់អ្នក ដើម្បី​ទទួល​បាន​ជំនួយ។"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"កម្រងព័ត៌មាន​ការងារ​របស់អ្នក​លែងមាន​នៅលើ​ឧបករណ៍​នេះទៀត​ហើយ"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ការព្យាយាមបញ្ចូលពាក្យសម្ងាត់ច្រើនដងពេកហើយ"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ឧបករណ៍ស្ថិតក្រោមការគ្រប់គ្រង"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ស្ថាប័នរបស់អ្នកគ្រប់គ្រងឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ។ ចុចដើម្បីទទួលបានព័ត៌មានលម្អិត។"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ឧបករណ៍របស់អ្នកនឹងត្រូវបានលុប"</string>
@@ -1792,4 +1791,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"មិន​អនុញ្ញាត​ចំពោះសីុម​ទេ"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"មិន​អនុញ្ញាត​ចំពោះទូរសព្ទ​ទេ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"វិនដូលេចឡើង"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index eaeb25b..c362ce1 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್‌ ತಪ್ಪಿಹೋಗಿರುವುದರಿಂದ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ಅಳಿಸಲಾಗಿದೆ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್ ಕಳೆದು ಹೋಗಿದೆ ಅಥವಾ ಹಾಳಾಗಿದೆ. ಇದರ ಪರಿಣಾಮವಾಗಿ ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಮತ್ತು ಅದಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಸಹಾಯಕ್ಕಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಈ ಸಾಧನದಲ್ಲಿ ಈಗ ಲಭ್ಯವಿಲ್ಲ"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ಹಲವಾರು ಪಾಸ್‌ವರ್ಡ್ ಪ್ರಯತ್ನಗಳು"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಅದು ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಗಮನವಿರಿಸಬಹುದು. ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"ಸಿಮ್‌ಗೆ ಅನುಮತಿಯಿಲ್ಲ"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ಫೋನ್‌ಗೆ ಅನುಮತಿಯಿಲ್ಲ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ಪಾಪ್‌ಅಪ್ ವಿಂಡೋ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 59fa0bf..991a983 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"관리 앱이 없어서 직장 프로필이 삭제되었습니다."</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"직장 프로필 관리 앱이 없거나 손상되어 직장 프로필 및 관련 데이터가 삭제되었습니다. 도움이 필요한 경우 관리자에게 문의하세요."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"직장 프로필을 이 기기에서 더 이상 사용할 수 없습니다."</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"비밀번호 입력을 너무 많이 시도함"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"관리되는 기기"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"조직에서 이 기기를 관리하며 네트워크 트래픽을 모니터링할 수도 있습니다. 자세한 내용을 보려면 탭하세요."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"기기가 삭제됩니다."</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM이 허용되지 않음"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"전화가 허용되지 않음"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"팝업 창"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index fb7b4f6..06bbabe 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Башкаруучу колдонмосу болбогондуктан, жумуш профили жок кылынды"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Жумуш профилинин башкаруучу колдонмосу жок же бузулгандыктан, жумуш профилиңиз жана ага байланыштуу дайындар жок кылынды. Жардам алуу үчүн администраторуңузга кайрылыңыз."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жумуш профилиңиз бул түзмөктөн жок кылынды"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Өтө көп жолу сырсөздү киргизүү аракети жасалды"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Түзмөктү ишкана башкарат"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын көрүү үчүн таптап коюңуз."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Түзмөгүңүз тазаланат"</string>
@@ -1791,4 +1790,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картаны колдонууга тыюу салынган"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонду колдонууга тыюу салынган"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Калкып чыкма терезе"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 477fbdc06..374188f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ SIM"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໂທລະສັບ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ໜ້າຈໍປັອບອັບ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2e44ed2..ab60a62 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Darbo profilis ištrintas dėl trūkstamos administratoriaus programos"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūksta darbo profilio administratoriaus programos arba ji sugadinta. Todėl darbo profilis ir susiję duomenys buvo ištrinti. Jei reikia pagalbos, susisiekite su administratoriumi."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Darbo profilis nebepasiekiamas šiame įrenginyje"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Per daug slaptažodžio bandymų"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Įrenginys yra tvarkomas"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Šį įrenginį tvarko organizacija ir gali stebėti tinklo srautą. Palieskite, kad gautumėte daugiau informacijos."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Įrenginys bus ištrintas"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kortelė neleidžiama"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonas neleidžiamas"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Iššokantysis langas"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 659c9a7..f64fe62 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -174,8 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Darba profils tika dzēsts, jo trūkst administratora lietotnes."</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūkst darba profila administratora lietotnes, vai šī lietotne ir bojāta. Šī iemesla dēļ jūsu darba profils un saistītie dati tika dzēsti. Lai saņemtu palīdzību, sazinieties ar administratoru."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jūsu darba profils šai ierīcē vairs nav pieejams."</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Veikts pārāk daudz paroles ievadīšanas mēģinājumu."</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Ierīce tiek pārvaldīta"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Jūsu organizācija pārvalda šo ierīci un var uzraudzīt tīkla datplūsmu. Pieskarieties, lai saņemtu detalizētu informāciju."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Jūsu ierīces dati tiks dzēsti"</string>
@@ -1825,4 +1824,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karti nav atļauts izmantot"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Tālruni nav atļauts izmantot"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Uznirstošais logs"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mcc001-mnc01/strings.xml b/core/res/res/values-mcc001-mnc01/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc001-mnc01/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc030/strings.xml b/core/res/res/values-mcc310-mnc030/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc030/strings.xml
+++ b/core/res/res/values-mcc310-mnc030/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc150/strings.xml b/core/res/res/values-mcc310-mnc150/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc150/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc170/strings.xml b/core/res/res/values-mcc310-mnc170/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc170/strings.xml
+++ b/core/res/res/values-mcc310-mnc170/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc280/strings.xml b/core/res/res/values-mcc310-mnc280/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc280/strings.xml
+++ b/core/res/res/values-mcc310-mnc280/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc410/strings.xml b/core/res/res/values-mcc310-mnc410/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc410/strings.xml
+++ b/core/res/res/values-mcc310-mnc410/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc560/strings.xml b/core/res/res/values-mcc310-mnc560/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc560/strings.xml
+++ b/core/res/res/values-mcc310-mnc560/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc950/strings.xml b/core/res/res/values-mcc310-mnc950/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc950/strings.xml
+++ b/core/res/res/values-mcc310-mnc950/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc311-mnc180/strings.xml b/core/res/res/values-mcc311-mnc180/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc311-mnc180/strings.xml
+++ b/core/res/res/values-mcc311-mnc180/strings.xml
@@ -20,4 +20,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
     <string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
 </resources>
diff --git a/core/res/res/values-mcc312-mnc670/strings.xml b/core/res/res/values-mcc312-mnc670/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc312-mnc670/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc313-mnc100/strings.xml b/core/res/res/values-mcc313-mnc100/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc313-mnc100/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a98e2f0..aef21c3 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1791,4 +1791,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Не е дозволена SIM-картичка"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Не е дозволен телефон"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Појавен прозорец"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 65b7041..89333cc 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"അഡ്‌മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നതിനാൽ ഔദ്യോഗിക പ്രൊഫൈൽ ഇല്ലാതാക്കി"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ഔദ്യോഗിക പ്രൊഫൈൽ അഡ്‌മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നു അല്ലെങ്കിൽ കേടായിരിക്കുന്നു. ഫലമായി, നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലും ബന്ധപ്പെട്ട വിവരങ്ങളും ഇല്ലാതാക്കിയിരിക്കുന്നു. സഹായത്തിന് അഡ്‌മിനെ ബന്ധപ്പെടുക."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ഈ ഉപകരണത്തിൽ തുടർന്നങ്ങോട്ട് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ലഭ്യമല്ല"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"വളരെയധികം പാസ്‌വേഡ് ശ്രമങ്ങൾ"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"നിങ്ങളുടെ ഉപകരണം മായ്‌ക്കും"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM അനുവദനീയമല്ല"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ഫോൺ അനുവദനീയമല്ല"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"പോപ്പ് അപ്പ് വിൻഡോ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b088180..9173f2c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Админ апп байхгүй байгаа тул ажлын профайлыг устгасан байна"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Ажлын профайлын админ апп байхгүй эсвэл эвдэрсэн байна. Үүний улмаас таны ажлын профайл болон холбогдох мэдээллийг устгасан болно. Тусламж хэрэгтэй бол админтай холбогдоно уу."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Таны ажлын профайл энэ төхөөрөмжид боломжгүй байна"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Нууц үгийг хэт олон удаа буруу оруулсан байна"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Төхөөрөмжийг удирдсан"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Таны байгууллага энэ төхөөрөмжийг удирдаж, сүлжээний ачааллыг хянадаг. Дэлгэрэнгүй мэдээлэл авах бол товшино уу."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Таны төхөөрөмж устах болно."</string>
@@ -1788,4 +1787,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM боломжгүй"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Утас боломжгүй"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"гэнэт гарч ирэх цонх"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9c74b30..eac23b9 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासक अॅप गहाळ असल्यामुळे कार्य प्रोफाइल हटवले गेले"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, आपले कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपले कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्‍ध नाही"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"बर्‍याचदा पासवर्ड टाकण्‍याचा प्रयत्‍न केला"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"आपली संस्था हे डिव्हाइस व्यवस्थापित करते आणि नेटवर्क रहदारीचे निरीक्षण करू शकते. तपशीलांसाठी टॅप करा."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"तुमचे डिव्हाइस मिटविले जाईल"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"सिमला अनुमती नाही"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"फोनला अनुमती नाही"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a81b76f..a627a05 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil kerja dipadamkan kerana ketiadaan apl pentadbir"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Apl pentadbir profil kerja tiada atau rosak. Akibatnya, profil kerja anda dan data yang berkaitan telah dipadamkan. Hubungi pentadbir anda untuk mendapatkan bantuan."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil kerja anda tidak lagi tersedia pada peranti ini"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Terlalu banyak percubaan kata laluan"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Peranti ini diurus"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasi anda mengurus peranti ini dan mungkin memantau trafik rangkaian. Ketik untuk mendapatkan butiran."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Peranti anda akan dipadam"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM tidak dibenarkan"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon tidak dibenarkan"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Tetingkap Timbul"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 34e783b..01f7b76 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"စီမံခန့်ခွဲရန် အက်ပ်မရှိသောကြောင့် အလုပ်ပရိုဖိုင်ကို ဖျက်လိုက်ခြင်းဖြစ်သည်"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"အလုပ်ပရိုဖိုင် စီမံခန့်ခွဲရန်အက်ပ် မရှိပါ သို့မဟုတ် ပျက်စီးနေပါသည်။ ထို့ကြောင့် သင်၏ အလုပ်ပရိုဖိုင်နှင့် ဆက်စပ်နေသော ဒေတာများကို ဖျက်လိုက်ပါပြီ။ အကူအညီရယူရန် သင်၏စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ဤစက်ပစ္စည်းတွင် သင်၏ အလုပ်ပရိုဖိုင်မရှိတော့ပါ"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"စကားဝှက်ထည့်သွင်းရန် ကြိုးစားသည့် အကြိမ်အရေအတွက် အလွန်များသွား၍ ဖြစ်ပါသည်"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"စက်ပစ္စည်းကို စီမံခန့်ခွဲထားပါသည်"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ဤစက်ပစ္စည်းကို သင်၏ အဖွဲ့အစည်းက စီမံပြီး ကွန်ရက်အသွားအလာကို စောင့်ကြည့်နိုင်ပါသည်။ ထပ်မံလေ့လာရန် တို့ပါ။"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"သင့်ကိရိယာအား ပယ်ဖျက်လိမ့်မည်"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"ဆင်းမ်ကို ခွင့်မပြုပါ"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ဖုန်းကို ခွင့်မပြုပါ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ပေါ့ပ်အပ် ဝင်းဒိုး"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 52436ea..8e7b683 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Jobbprofilen er slettet på grunn av manglende administratorapp"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorappen for jobbprofilen mangler eller er skadet. Dette har ført til at jobbprofilen og alle data knyttet til den, har blitt slettet. Ta kontakt med administratoren for å få hjelp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jobbprofilen din er ikke lenger tilgjengelig på denne enheten"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"For mange passordforsøk"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Enheten administreres"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasjonen din kontrollerer denne enheten og kan overvåke nettverkstrafikk. Trykk for å få mer informasjon."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheten blir slettet"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortet er ikke tillatt"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonen er ikke tillatt"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Forgrunnsvindu"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index bb84eae..41bb71b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासकीय अनुप्रयोग नभएकाले कार्य प्रोफाइल मेटाइयो"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"उक्त कार्य प्रोफाइलको प्रशासकीय अनुप्रयोग छैन वा बिग्रेको छ। त्यसले गर्दा, तपाईंको कार्य प्रोफाइल र सम्बन्धित डेटालाई मेटिएको छ। सहायताका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तपाईंको कार्य प्रोफाइल अब उप्रान्त यस यन्त्रमा उपलब्ध छैन"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"पासवर्ड प्रविष्ट गर्ने अत्यधिक गलत प्रयासहरू भए"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"यन्त्र व्यवस्थित गरिएको छ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"तपाईंको संगठनले यस यन्त्रको व्यवस्थापन गर्दछ र नेटवर्क ट्राफिकको अनुगमन गर्न सक्छ। विवरणहरूका लागि ट्याप गर्नुहोस्।"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"तपाईंको यन्त्र मेटिनेछ"</string>
@@ -1796,4 +1795,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM लाई अनुमति छैन"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"फोनलाई अनुमति छैन"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"पपअप विन्डो"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3f8ed59..1355ed3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Simkaart niet toegestaan"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefoon niet toegestaan"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-upvenster"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 2c0ba4b..d6ecf91 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ਗੁੰਮਸ਼ੁਦਾ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਦੇ ਕਾਰਨ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਮਿਟਾਇਆ ਗਿਆ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਜਾਂ ਤਾਂ ਗੁੰਮਸ਼ੁਦਾ ਹੈ ਜਾਂ ਖਰਾਬ ਹੈ। ਨਤੀਜੇ ਵਜੋਂ, ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਅਤੇ ਸਬੰਧਿਤ ਡਾਟਾ ਮਿਟਾਇਆ ਗਿਆ ਹੈ। ਸਹਾਇਤਾ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਹੁਣ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ਕਈ ਵਾਰ ਗਲਤ ਪਾਸਵਰਡ ਦਾਖਲ ਕੀਤਾ ਗਿਆ"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਮਿਟਾਇਆ ਜਾਏਗਾ"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ਫ਼ੋਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"ਪੌਪਅੱਪ ਵਿੰਡੋ"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 685ad53..5904abd 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil do pracy został usunięty z powodu braku aplikacji administratora"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Brakuje aplikacji administratora profilu do pracy lub jest ona uszkodzona. Dlatego Twój profil do pracy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Twój profil do pracy nie jest już dostępny na tym urządzeniu"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Zbyt wiele prób podania hasła"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Urządzenie jest zarządzane"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Twoja organizacja zarządza tym urządzeniem i może monitorować ruch w sieci. Kliknij, by dowiedzieć się więcej."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Twoje urządzenie zostanie wyczyszczone"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Niedozwolona karta SIM"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Niedozwolony telefon"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Wyskakujące okienko"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 1a9eefb..23b0eef 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone não permitido"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 0f4635b..141c8f0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telemóvel não permitido"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 1a9eefb..23b0eef 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone não permitido"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 298434c..7cf8b1a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1824,4 +1824,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Cardul SIM nu este permis"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonul nu este permis"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Fereastră pop-up"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 643978e..b8267c3 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Рабочий профиль удален, поскольку отсутствует приложение для администрирования"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Приложение для администрирования рабочего профиля отсутствует или повреждено. Из-за этого рабочий профиль и связанные с ним данные были удалены. Если у вас возникли вопросы, обратитесь к администратору."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш рабочий профиль больше не доступен на этом устройстве"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Слишком много попыток ввести пароль."</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Это управляемое устройство"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик. Подробнее…"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Все данные с устройства будут удалены"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Использование SIM-карты запрещено"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Звонки запрещены"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Всплывающее окно"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index e8b963c..bb9e6ec 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1791,4 +1791,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM එක සඳහා ඉඩ නොදේ"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"දුරකථනය සඳහා ඉඩ නොදේ"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"උත්පතන කවුළුව"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 36ad8b9..8646e95 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -176,8 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovný profil bol odstránený z dôvodu chýbajúcej aplikácie na správu"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikácia na správu pracovného profilu buď chýba, alebo je poškodená. Z toho dôvodu bol odstránený pracovný profil aj k nemu priradené dáta. Ak potrebujete pomoc, kontaktujte svojho správcu."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovný profil už v tomto zariadení nie je k dispozícii"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Príliš veľa pokusov o zadanie hesla"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Zariadenie je spravované"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku. Klepnutím zobrazíte podrobnosti."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Vaše zariadenie bude vymazané"</string>
@@ -1860,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta je zakázaná"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefón je zakázaný"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Automaticky otvárané okno"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7ad6bc2..da2a261 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1859,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Kartica SIM ni dovoljena"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon ni dovoljen"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pojavno okno"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 7f0a693..0669d4c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profili i punës u fshi për shkak të mungesës së aplikacionit të administratorit"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacioni i administratorit të profilit të punës mungon ose është dëmtuar. Si rezultat i kësaj, profili yt i punës dhe të dhënat përkatëse janë fshirë. Kontakto me administratorin për ndihmë."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profili yt i punës nuk është më i disponueshëm në këtë pajisje"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Shumë përpjekje për fjalëkalimin"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Pajisja është e menaxhuar"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organizata jote e menaxhon këtë pajisje dhe mund të monitorojë trafikun e rrjetit. Trokit për detaje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Pajisja do të spastrohet"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Karta SIM nuk lejohet"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefoni nuk lejohet"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Dritare kërcyese"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 1008215..26d7a298 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -174,8 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Профил за Work је избрисан јер недостаје апликација за администраторе"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Апликација за администраторе на профилу за Work недостаје или је оштећена. Због тога су профил за Work и повезани подаци избрисани. Обратите се администратору за помоћ."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Профил за Work више није доступан на овом уређају"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Превише покушаја уноса лозинке"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Уређајем се управља"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Уређај ће бити обрисан"</string>
@@ -1825,4 +1824,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картица није дозвољена"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефон није дозвољен"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Искачући прозор"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 44a66f8..5b3fdf6 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kort tillåts inte"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Mobil tillåts inte"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"popup-fönster"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 9a78fd4..78fc186 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1787,4 +1787,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM imekataliwa"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Simu imekataliwa"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Dirisha Ibukizi"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 1cd5cd0..544d109 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"நிர்வாகிப் பயன்பாடு இல்லாததால், பணி விவரம் நீக்கப்பட்டது"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"பணி விவர நிர்வாகிப் பயன்பாடு இல்லை அல்லது அது சிதைந்துள்ளது. இதன் விளைவாக, உங்கள் பணி விவரமும் அதனுடன் தொடர்புடைய தரவும் நீக்கப்பட்டன. உதவிக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"இந்தச் சாதனத்தில் இனி பணி விவரம் கிடைக்காது"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"கடவுச்சொல்லை அதிக முறை தவறாக முயற்சித்துவிட்டீர்கள்"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"சாதனம் நிர்வகிக்கப்படுகிறது"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"உங்கள் நிறுவனம் இந்தச் சாதனத்தை நிர்வகிக்கும், அத்துடன் அது நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம். விவரங்களுக்கு, தட்டவும்."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"சாதனத் தரவு அழிக்கப்படும்"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"சிம் அனுமதிக்கப்படவில்லை"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ஃபோன் அனுமதிக்கப்படவில்லை"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"பாப்அப் சாளரம்"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9c1a529..75c37249 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"నిర్వాహక యాప్ లేనందున కార్యాలయ ప్రొఫైల్ తొలగించబడింది"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"కార్యాలయ ప్రొఫైల్ నిర్వాహక యాప్ లేదు లేదా పాడైంది. తత్ఫలితంగా, మీ కార్యాలయ ప్రొఫైల్ మరియు సంబంధిత డేటా తొలగించబడ్డాయి. సహాయం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"చాలా ఎక్కువ పాస్‌వర్డ్ ప్రయత్నాలు చేసారు"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"పరికరం నిర్వహించబడింది"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"మీ పరికరంలోని డేటా తొలగించబడుతుంది"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM అనుమతించబడదు"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ఫోన్ అనుమతించబడదు"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"పాప్అప్ విండో"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 900f950..66a7280 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ลบโปรไฟล์งานแล้วเนื่องจากไม่มีแอปผู้ดูแลระบบ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"แอปผู้ดูแลระบบโปรไฟล์งานไม่มีอยู่หรือเสียหาย ระบบจึงทำการลบโปรไฟล์งานและข้อมูลที่เกี่ยวข้องของคุณออก โปรดติดต่อผู้ดูแลระบบเพื่อรับความช่วยเหลือ"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"โปรไฟล์งานของคุณไม่สามารถใช้ในอุปกรณ์นี้อีกต่อไป"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ลองป้อนรหัสผ่านหลายครั้งเกินไป"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"อุปกรณ์มีการจัดการ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"องค์กรของคุณจัดการอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย แตะเพื่อดูรายละเอียด"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ระบบจะลบข้อมูลในอุปกรณ์ของคุณ"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"ไม่อนุญาตให้ใช้ซิม"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"ไม่อนุญาตให้ใช้โทรศัพท์"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"หน้าต่างป๊อปอัป"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 267f2ed..bafcef3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Na-delete ang profile sa trabaho dahil wala itong admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Nawawala o nasira ang admin app ng profile sa trabaho. Dahil dito, na-delete ang profile mo sa trabaho at nauugnay na data. Makipag-ugnayan sa iyong admin para sa tulong."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hindi na available sa device na ito ang iyong profile sa trabaho"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Masyadong maraming pagsubok sa password"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Pinamamahalaan ang device"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Pinamamahalaan ng iyong organisasyon ang device na ito, at maaari nitong subaybayan ang trapiko sa network. I-tap para sa mga detalye."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Buburahin ang iyong device"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"Hindi pinahihintulutan ang SIM"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Hindi pinahihintulutan ang telepono"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Window ng Popup"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3d592fe..7e2d2bfe 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Eksik yönetici uygulaması nedeniyle iş profili silindi"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"İş profili yönetici uygulaması eksik ya da bozuk. Bunun sonucunda iş profiliniz ve ilgili veriler silindi. Yardım almak için yöneticiniz ile iletişim kurun."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"İş profiliniz arık bu cihazda kullanılamıyor"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Çok fazla şifre denemesi yapıldı"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Cihaz yönetiliyor"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Kuruluşunuz bu cihazı yönetmekte olup ağ trafiğini izleyebilir. Ayrıntılar için dokunun."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Cihazınız silinecek"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM\'e izin verilmiyor"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefona izin verilmiyor"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Pop-up Pencere"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index a00f488..050b26b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1859,4 +1859,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-карта заборонена"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефон заборонено"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Спливаюче вікно"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 4e528bd..44e7350 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"گمشدہ منتظم ایپ کی وجہ سے دفتری پروفائل حذف کر دیا گیا"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"دفتری پروفائل کی منتظم ایپ یا تو غائب ہے یا خراب ہے۔ اس کی وجہ سے، آپ کا دفتری پروفائل اور متعلقہ ڈیٹا حذف کر دیے گئے ہیں۔ مدد کیلئے اپنے منتظم سے رابطہ کریں۔"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"آپ کا دفتری پروفائل اس آلہ پر مزید دستیاب نہیں ہے"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"پاس ورڈ کی بہت ساری کوششیں"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"آلہ زیر انتظام ہے"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"آپ کی تنظیم اس آلے کا نظم کرتی ہے اور وہ نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے۔ تفاصیل کیلئے تھپتھپائیں۔"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"آپ کا آلہ صاف کر دیا جائے گا"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"‏SIM کی اجازت نہیں ہے"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"فون کی اجازت نہیں ہے"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"پاپ اپ ونڈو"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a269630..b62d5b9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta ishlatish taqiqlangan"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Chaqiruvlar taqiqlangan"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Qalqib chiquvchi oyna"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 09df823..921f7c6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Đã xóa hồ sơ công việc do thiếu ứng dụng quản trị"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Ứng dụng quản trị hồ sơ công việc bị thiếu hoặc hỏng. Do vậy, hồ sơ công việc của bạn và dữ liệu liên quan đã bị xóa. Hãy liên hệ với quản trị viên của bạn để được trợ giúp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hồ sơ công việc của bạn không có sẵn trên thiết bị này nữa"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Quá nhiều lần nhập mật khẩu"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Thiết bị được quản lý"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Tổ chức của bạn sẽ quản lý thiết bị này và có thể theo dõi lưu lượng truy cập mạng. Nhấn để biết chi tiết."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Thiết bị của bạn sẽ bị xóa"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM không được cho phép"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Điện thoại không được cho phép"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Cửa sổ bật lên"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b3c1af5..06b781d 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"由于缺少管理应用,工作资料已被删除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"工作资料管理应用缺失或损坏,因此系统已删除您的工作资料及相关数据。如需帮助,请与您的管理员联系。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作资料已不在此设备上"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密码尝试次数过多"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"设备为受管理设备"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"系统将清空您的设备"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"不受允许的 SIM 卡"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"不受允许的手机"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"弹出式窗口"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 04a62a6..aac5fcc 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"由於沒有管理員應用程式,工作設定檔已刪除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"工作設定檔管理員應用程式已遺失或損毀。因此,您的工作設定檔和相關資料已刪除。請聯絡您的管理員以取得協助。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作設定檔無法再在此裝置上使用"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密碼輸入錯誤的次數過多"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"裝置已受管理"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"您的機構會管理此裝置,並可能會監控網絡流量。輕按即可瞭解詳情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"您的裝置將被清除"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"不允許使用 SIM 卡"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"不允許使用手機"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"彈出式視窗"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0e9827a..6a35994 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -172,8 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work 設定檔因管理員應用程式遺失而遭到刪除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Work 設定檔管理員應用程式遺失或已毀損,因此系統刪除了你的 Work 設定檔和相關資料。如需協助,請與你的管理員聯絡。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"你的 Work 設定檔已不在這個裝置上"</string>
-    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
-    <skip />
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密碼輸入錯誤的次數過多"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"裝置受到管理"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"貴機構會管理這個裝置,且可能監控網路流量。輕觸即可瞭解詳情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"你的裝置資料將遭到清除"</string>
@@ -1790,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"不受允許的 SIM 卡"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"不受允許的手機"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"彈出式視窗"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f341da1..6f5e4cb 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1789,4 +1789,6 @@
     <string name="mmcc_illegal_ms" msgid="2769452751852211112">"I-SIM ayivunyelwe"</string>
     <string name="mmcc_illegal_me" msgid="4438696681169345015">"Ifoni ayivunyelwe"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Iwindi lesigelekeqe"</string>
+    <!-- no translation found for slice_more_content (8504342889413274608) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 11cdb76..4b9839f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2546,16 +2546,24 @@
         <attr name="filterTouchesWhenObscured" format="boolean" />
 
         <!-- Defines the quality of translucent drawing caches. This property is used
-             only when the drawing cache is enabled and translucent. The default value is auto. -->
+             only when the drawing cache is enabled and translucent. The default value is auto.
+             Deprecated: The view drawing cache was largely made obsolete with the introduction of
+             hardware-accelerated rendering in API 11. -->
         <attr name="drawingCacheQuality">
             <!-- Lets the framework decide what quality level should be used
-                 for the drawing cache. -->
+                 for the drawing cache.
+                 Deprecated: The view drawing cache was largely made obsolete with the introduction
+                 of hardware-accelerated rendering in API 11. -->
             <enum name="auto" value="0" />
             <!-- Low quality. When set to low quality, the drawing cache uses a lower color
-                 depth, thus losing precision in rendering gradients, but uses less memory. -->
+                 depth, thus losing precision in rendering gradients, but uses less memory.
+                 Deprecated: The view drawing cache was largely made obsolete with the introduction
+                 of hardware-accelerated rendering in API 11. -->
             <enum name="low" value="1" />
             <!-- High quality. When set to high quality, the drawing cache uses a higher
-                 color depth but uses more memory. -->
+                 color depth but uses more memory.
+                 Deprecated: The view drawing cache was largely made obsolete with the introduction
+                 of hardware-accelerated rendering in API 11. -->
             <enum name="high" value="2" />
         </attr>
 
@@ -3060,7 +3068,9 @@
              instance during a scrolling.) This property lets you persist the cache
              in memory after its initial usage. Persisting the cache consumes more
              memory but may prevent frequent garbage collection is the cache is created
-             over and over again. By default the persistence is set to scrolling. -->
+             over and over again. By default the persistence is set to scrolling.
+             Deprecated: The view drawing cache was largely made obsolete with the introduction of
+             hardware-accelerated rendering in API 11. -->
         <attr name="persistentDrawingCache">
             <!-- The drawing cache is not persisted after use. -->
             <flag name="none" value="0x0" />
@@ -3072,7 +3082,9 @@
             <flag name="all" value="0x3" />
         </attr>
         <!-- Defines whether the ViewGroup should always draw its children using their
-             drawing cache or not. The default value is true. -->
+             drawing cache or not. The default value is true.
+             Deprecated: The view drawing cache was largely made obsolete with the introduction of
+             hardware-accelerated rendering in API 11. -->
         <attr name="alwaysDrawnWithCache" format="boolean" />
         <!-- Sets whether this ViewGroup's drawable states also include
              its children's drawable states.  This is used, for example, to
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e6c829f..0e90287 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1449,8 +1449,7 @@
              quit it.  Only one such app can be running at a time; if the user
              tries to launch a second such app, they will be prompted
              to quit the first before doing so.  While the
-             application is running, the user will be informed of this.
-             @hide -->
+             application is running, the user will be informed of this. -->
         <attr name="cantSaveState" format="boolean" />
         <attr name="uiOptions" />
         <!-- Declare that your application will be able to deal with RTL (right to left) layouts.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dcb56a2..570f37c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2352,9 +2352,37 @@
     <!-- Package name for default network scorer app; overridden by product overlays. -->
     <string name="config_defaultNetworkScorerPackageName"></string>
 
-    <!-- default device has recents property -->
+    <!-- Determines whether recent tasks are provided to the user. Default device has recents
+         property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
 
+    <!-- The minimum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no minimum limit. -->
+    <integer name="config_minNumVisibleRecentTasks_grid">-1</integer>
+
+    <!-- The maximum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no maximum limit. -->
+    <integer name="config_maxNumVisibleRecentTasks_grid">9</integer>
+
+    <!-- The minimum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no minimum limit. -->
+    <integer name="config_minNumVisibleRecentTasks_lowRam">-1</integer>
+
+    <!-- The maximum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no maximum limit. -->
+    <integer name="config_maxNumVisibleRecentTasks_lowRam">9</integer>
+
+    <!-- The minimum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no minimum limit. -->
+    <integer name="config_minNumVisibleRecentTasks">5</integer>
+
+    <!-- The maximum number of visible recent tasks to be presented to the user through the
+         SystemUI. Can be -1 if there is no maximum limit. -->
+    <integer name="config_maxNumVisibleRecentTasks">-1</integer>
+
+    <!-- The duration in which a recent task is considered in session and should be visible. -->
+    <integer name="config_activeTaskDurationHours">6</integer>
+
     <!-- default window ShowCircularMask property -->
     <bool name="config_windowShowCircularMask">false</bool>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 108ac1c..0364b81 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2745,18 +2745,6 @@
 
   <!-- ===============================================================
        Resources added in version O of the platform
-
-       NOTE: add <public> elements within a <public-group> like so:
-
-       <public-group type="attr" first-id="0x01010531">
-           <public name="exampleAttr1" />
-           <public name="exampleAttr2" />
-       </public-group>
-
-       To add a new public-group block, choose an id value that is 1 greater
-       than the last of that item above. For example, the last "attr" id
-       value above is 0x01010530, so the public-group of attrs below has
-       the id value of 0x01010531.
        =============================================================== -->
     <eat-comment />
 
@@ -2827,18 +2815,6 @@
 
   <!-- ===============================================================
        Resources added in version O MR1 of the platform
-
-       NOTE: add <public> elements within a <public-group> like so:
-
-       <public-group type="attr" first-id="0x01010531">
-           <public name="exampleAttr1" />
-           <public name="exampleAttr2" />
-       </public-group>
-
-       To add a new public-group block, choose an id value that is 1 greater
-       than the last of that item above. For example, the last "attr" id
-       value above is 0x01010530, so the public-group of attrs below has
-       the id value of 0x01010531.
        =============================================================== -->
     <eat-comment />
 
@@ -2868,6 +2844,7 @@
     <eat-comment />
 
     <public-group type="attr" first-id="0x0101056e">
+      <public name="cantSaveState" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5189e7f..085241a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -591,6 +591,11 @@
     <!-- Text shown when viewing channel settings for notifications related to a usb connection -->
     <string name="notification_channel_usb">USB connection</string>
 
+    <!-- Text shown when viewing channel settings for notification about a heavy-weight app
+         currently running.
+         [CHAR_LIMIT=NONE] -->
+    <string name="notification_channel_heavy_weight_app">App running</string>
+
     <!-- This is the label for the notification channel settings that controls the behavior
         of the notification about applications that are running in the background (that is,
         perhaps confusingly, running foreground services but not the foreground UI on the screen).
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 53d09d8..5ac120b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -311,6 +311,13 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="bool" name="config_hasRecents" />
+  <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
+  <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
+  <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
+  <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_grid" />
+  <java-symbol type="integer" name="config_minNumVisibleRecentTasks" />
+  <java-symbol type="integer" name="config_maxNumVisibleRecentTasks" />
+  <java-symbol type="integer" name="config_activeTaskDurationHours" />
   <java-symbol type="bool" name="config_windowShowCircularMask" />
   <java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" />
   <java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" />
@@ -3033,6 +3040,7 @@
   <java-symbol type="string" name="notification_channel_alerts" />
   <java-symbol type="string" name="notification_channel_retail_mode" />
   <java-symbol type="string" name="notification_channel_usb" />
+  <java-symbol type="string" name="notification_channel_heavy_weight_app" />
   <java-symbol type="string" name="config_defaultAutofillService" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 5f4199a..1e4c03e 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -19,5 +19,5 @@
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
 <zen version="2">
-    <allow calls="true" messages="false" reminders="true" events="true" />
+    <allow alarms="true" media_system_other="true" calls="false" messages="false" reminders="false" events="false" />
 </zen>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index dbc9e5d..15eab1f 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,7 +36,8 @@
     ub-uiautomator \
     platform-test-annotations \
     compatibility-device-util \
-    truth-prebuilt
+    truth-prebuilt \
+    print-test-util-lib
 
 LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ac5d224..9c0543b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -102,6 +102,7 @@
     <!-- os storage test permissions -->
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.ASEC_ACCESS" />
+    <uses-permission android:name="android.permission.ASEC_ACCESS" />
     <uses-permission android:name="android.permission.ASEC_CREATE" />
     <uses-permission android:name="android.permission.ASEC_DESTROY" />
     <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" />
@@ -1345,10 +1346,12 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.print.PrintTestActivity"/>
+        <activity
+            android:name="android.print.test.PrintDocumentActivity"
+            android:theme="@style/Theme" />
 
         <service
-            android:name="android.print.mockservice.MockPrintService"
+            android:name="android.print.test.services.FirstPrintService"
             android:permission="android.permission.BIND_PRINT_SERVICE">
             <intent-filter>
                 <action android:name="android.printservice.PrintService" />
@@ -1360,9 +1363,10 @@
         </service>
 
         <activity
-            android:name="android.print.mockservice.SettingsActivity"
+            android:name="android.print.test.services.SettingsActivity"
             android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
-            android:exported="true">
+            android:exported="true"
+            android:theme="@style/Theme">
         </activity>
 
         <activity
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
deleted file mode 100644
index a70c604..0000000
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ /dev/null
@@ -1,324 +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.print;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.print.mockservice.PrintServiceCallbacks;
-import android.print.mockservice.PrinterDiscoverySessionCallbacks;
-import android.print.mockservice.StubbablePrinterDiscoverySession;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.uiautomator.UiDevice;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.mockito.stubbing.Answer;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This is the base class for print tests.
- */
-abstract class BasePrintTest {
-    protected static final long OPERATION_TIMEOUT = 30000;
-    private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
-    private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
-
-    private android.print.PrintJob mPrintJob;
-
-    private CallCounter mStartCallCounter;
-    private CallCounter mStartSessionCallCounter;
-
-    private static Instrumentation sInstrumentation;
-    private static UiDevice sUiDevice;
-
-    @Rule
-    public ActivityTestRule<PrintTestActivity> mActivityRule =
-            new ActivityTestRule<>(PrintTestActivity.class, false, true);
-
-    /**
-     * {@link Runnable} that can throw and {@link Exception}
-     */
-    interface Invokable {
-        /**
-         * Execute the invokable
-         *
-         * @throws Exception
-         */
-        void run() throws Exception;
-    }
-
-    /**
-     * Assert that the invokable throws an expectedException
-     *
-     * @param invokable The {@link Invokable} to run
-     * @param expectedClass The {@link Exception} that is supposed to be thrown
-     */
-    void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
-            throws Exception {
-        try {
-            invokable.run();
-        } catch (Exception e) {
-            if (e.getClass().isAssignableFrom(expectedClass)) {
-                return;
-            } else {
-                throw e;
-            }
-        }
-
-        throw new AssertionError("No exception thrown");
-    }
-
-    /**
-     * Return the UI device
-     *
-     * @return the UI device
-     */
-    public UiDevice getUiDevice() {
-        return sUiDevice;
-    }
-
-    protected static Instrumentation getInstrumentation() {
-        return sInstrumentation;
-    }
-
-    @BeforeClass
-    public static void setUpClass() throws Exception {
-        sInstrumentation = InstrumentationRegistry.getInstrumentation();
-        assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_PRINTING));
-
-        sUiDevice = UiDevice.getInstance(sInstrumentation);
-
-        // Make sure we start with a clean slate.
-        clearPrintSpoolerData();
-
-        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
-        // Dexmaker is used by mockito.
-        System.setProperty("dexmaker.dexcache", getInstrumentation()
-                .getTargetContext().getCacheDir().getPath());
-    }
-
-    @Before
-    public void initCounters() throws Exception {
-        // Initialize the latches.
-        mStartCallCounter = new CallCounter();
-        mStartSessionCallCounter = new CallCounter();
-    }
-
-    @Before
-    public void unlockScreen() throws Exception {
-        // Unlock screen.
-        runShellCommand(getInstrumentation(), "input keyevent KEYCODE_WAKEUP");
-        runShellCommand(getInstrumentation(), "wm dismiss-keyguard");
-    }
-
-    @After
-    public void exitActivities() throws Exception {
-        // Exit print spooler
-        getUiDevice().pressBack();
-        getUiDevice().pressBack();
-    }
-
-    protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
-            final PrintAttributes attributes) {
-        // Initiate printing as if coming from the app.
-        getInstrumentation().runOnMainSync(() -> {
-            PrintManager printManager = (PrintManager) getActivity()
-                    .getSystemService(Context.PRINT_SERVICE);
-            mPrintJob = printManager.print("Print job", adapter, attributes);
-        });
-
-        return mPrintJob;
-    }
-
-    protected void onStartCalled() {
-        mStartCallCounter.call();
-    }
-
-    protected void onPrinterDiscoverySessionStartCalled() {
-        mStartSessionCallCounter.call();
-    }
-
-    protected void waitForPrinterDiscoverySessionStartCallbackCalled() {
-        waitForCallbackCallCount(mStartSessionCallCounter, 1,
-                "Did not get expected call to onStartPrinterDiscoverySession.");
-    }
-
-    protected void waitForStartAdapterCallbackCalled() {
-        waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start.");
-    }
-
-    private static void waitForCallbackCallCount(CallCounter counter, int count, String message) {
-        try {
-            counter.waitForCount(count, OPERATION_TIMEOUT);
-        } catch (TimeoutException te) {
-            fail(message);
-        }
-    }
-
-    protected PrintTestActivity getActivity() {
-        return mActivityRule.getActivity();
-    }
-
-    public static String runShellCommand(Instrumentation instrumentation, String cmd)
-            throws IOException {
-        ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
-        byte[] buf = new byte[512];
-        int bytesRead;
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        StringBuilder stdout = new StringBuilder();
-        while ((bytesRead = fis.read(buf)) != -1) {
-            stdout.append(new String(buf, 0, bytesRead));
-        }
-        fis.close();
-        return stdout.toString();
-    }
-
-    protected static void clearPrintSpoolerData() throws Exception {
-        assertTrue("failed to clear print spooler data",
-                runShellCommand(getInstrumentation(), String.format(
-                        "pm clear --user %d %s", CURRENT_USER_ID,
-                        PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
-                        .contains(PM_CLEAR_SUCCESS_OUTPUT));
-    }
-
-    @SuppressWarnings("unchecked")
-    protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
-            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
-            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
-            Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
-            Answer<Void> onDestroy) {
-        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
-
-        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
-        when(callbacks.getSession()).thenCallRealMethod();
-
-        if (onStartPrinterDiscovery != null) {
-            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
-                    any(List.class));
-        }
-        if (onStopPrinterDiscovery != null) {
-            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
-        }
-        if (onValidatePrinters != null) {
-            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
-                    any(List.class));
-        }
-        if (onStartPrinterStateTracking != null) {
-            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onRequestCustomPrinterIcon != null) {
-            doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
-                    any(PrinterId.class), any(CancellationSignal.class),
-                    any(CustomPrinterIconCallback.class));
-        }
-        if (onStopPrinterStateTracking != null) {
-            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onDestroy != null) {
-            doAnswer(onDestroy).when(callbacks).onDestroy();
-        }
-
-        return callbacks;
-    }
-
-    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
-            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
-            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
-        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
-
-        doCallRealMethod().when(service).setService(any(PrintService.class));
-        when(service.getService()).thenCallRealMethod();
-
-        if (onCreatePrinterDiscoverySessionCallbacks != null) {
-            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
-                    .onCreatePrinterDiscoverySessionCallbacks();
-        }
-        if (onPrintJobQueued != null) {
-            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
-        }
-        if (onRequestCancelPrintJob != null) {
-            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
-                    any(PrintJob.class));
-        }
-
-        return service;
-    }
-
-    private static final class CallCounter {
-        private final Object mLock = new Object();
-
-        private int mCallCount;
-
-        public void call() {
-            synchronized (mLock) {
-                mCallCount++;
-                mLock.notifyAll();
-            }
-        }
-
-        int getCallCount() {
-            synchronized (mLock) {
-                return mCallCount;
-            }
-        }
-
-        public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
-            synchronized (mLock) {
-                final long startTimeMillis = SystemClock.uptimeMillis();
-                while (mCallCount < count) {
-                    try {
-                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                        final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
-                        if (remainingTimeMillis <= 0) {
-                            throw new TimeoutException();
-                        }
-                        mLock.wait(timeoutMillis);
-                    } catch (InterruptedException ie) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 45e3f67..5d12f7e 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -16,6 +16,8 @@
 
 package android.print;
 
+import static android.print.test.Utils.assertException;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -33,10 +35,11 @@
 import android.print.PrintAttributes.Margins;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
-import android.print.mockservice.MockPrintService;
-import android.print.mockservice.PrintServiceCallbacks;
-import android.print.mockservice.PrinterDiscoverySessionCallbacks;
-import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.recommendation.IRecommendationsChangeListener;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -149,7 +152,7 @@
 
                         session.addPrinters(printers);
                     }
-                    onPrinterDiscoverySessionStartCalled();
+                    onPrinterDiscoverySessionCreateCalled();
                     return null;
                 }, null, null, null, null, null, null),
                 null, null);
@@ -200,13 +203,18 @@
     }
 
     private void startPrinting() {
-        mGoodPrintJob = print(createMockAdapter(), null);
+        mGoodPrintJob = print(createMockAdapter(), (PrintAttributes) null);
 
         // Wait for PrintActivity to be ready
-        waitForStartAdapterCallbackCalled();
+        waitForAdapterStartCallbackCalled();
 
         // Wait for printer discovery session to be ready
-        waitForPrinterDiscoverySessionStartCallbackCalled();
+        waitForPrinterDiscoverySessionCreateCallbackCalled();
+    }
+
+    private void endPrinting() {
+        getUiDevice().pressBack();
+        getUiDevice().pressBack();
     }
 
     /**
@@ -220,7 +228,7 @@
 
     @Before
     public void setUpMockService() throws Exception {
-        MockPrintService.setCallbacks(createMockCallbacks());
+        FirstPrintService.setCallbacks(createMockCallbacks());
 
         mIPrintManager = IPrintManager.Stub
                 .asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
@@ -231,7 +239,7 @@
      */
     @LargeTest
     @Test
-    public void testGetPrintJobInfo() throws Exception {
+    public void testGetPrintJobInfo() throws Throwable {
         startPrinting();
 
         assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
@@ -244,6 +252,8 @@
                 SecurityException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -251,7 +261,7 @@
      */
     @LargeTest
     @Test
-    public void testGetPrintJobInfos() throws Exception {
+    public void testGetPrintJobInfos() throws Throwable {
         startPrinting();
 
         List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
@@ -269,6 +279,8 @@
                 SecurityException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -276,7 +288,7 @@
      */
     @LargeTest
     @Test
-    public void testPrint() throws Exception {
+    public void testPrint() throws Throwable {
         final String name = "dummy print job";
 
         final IPrintDocumentAdapter adapter = new PrintManager
@@ -303,6 +315,8 @@
                 getActivity().getPackageName(), BAD_APP_ID, mUserId), SecurityException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -310,7 +324,7 @@
      */
     @LargeTest
     @Test
-    public void testCancelPrintJob() throws Exception {
+    public void testCancelPrintJob() throws Throwable {
         startPrinting();
 
         // Invalid print jobs IDs do not produce an exception
@@ -325,6 +339,8 @@
 
         // Must be last as otherwise mGoodPrintJob will not be good anymore
         mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+
+        endPrinting();
     }
 
     /**
@@ -332,7 +348,7 @@
      */
     @LargeTest
     @Test
-    public void testRestartPrintJob() throws Exception {
+    public void testRestartPrintJob() throws Throwable {
         startPrinting();
 
         mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
@@ -346,6 +362,8 @@
                 SecurityException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -353,7 +371,8 @@
      */
     @MediumTest
     @Test
-    public void testAddPrintJobStateChangeListener() throws Exception {
+    @NoActivity
+    public void testAddPrintJobStateChangeListener() throws Throwable {
         final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
 
         mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
@@ -373,7 +392,8 @@
      */
     @MediumTest
     @Test
-    public void testRemovePrintJobStateChangeListener() throws Exception {
+    @NoActivity
+    public void testRemovePrintJobStateChangeListener() throws Throwable {
         final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
 
         mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
@@ -394,7 +414,8 @@
      */
     @MediumTest
     @Test
-    public void testAddPrintServicesChangeListener() throws Exception {
+    @NoActivity
+    public void testAddPrintServicesChangeListener() throws Throwable {
         final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
 
         assertException(() ->  mIPrintManager.addPrintServicesChangeListener(listener, mUserId),
@@ -411,7 +432,8 @@
      */
     @MediumTest
     @Test
-    public void testRemovePrintServicesChangeListener() throws Exception {
+    @NoActivity
+    public void testRemovePrintServicesChangeListener() throws Throwable {
         final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
 
         assertException(() ->  mIPrintManager.removePrintServicesChangeListener(listener, mUserId),
@@ -426,7 +448,8 @@
      */
     @MediumTest
     @Test
-    public void testGetPrintServices() throws Exception {
+    @NoActivity
+    public void testGetPrintServices() throws Throwable {
         assertException(() -> mIPrintManager.getPrintServices(PrintManager.ALL_SERVICES, mUserId),
                 SecurityException.class);
 
@@ -444,7 +467,8 @@
      */
     @MediumTest
     @Test
-    public void testSetPrintServiceEnabled() throws Exception {
+    @NoActivity
+    public void testSetPrintServiceEnabled() throws Throwable {
         assertException(
                 () -> mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true,
                                 mUserId), SecurityException.class);
@@ -460,7 +484,8 @@
      */
     @MediumTest
     @Test
-    public void testAddPrintServiceRecommendationsChangeListener() throws Exception {
+    @NoActivity
+    public void testAddPrintServiceRecommendationsChangeListener() throws Throwable {
         final IRecommendationsChangeListener listener =
                 createMockIPrintServiceRecommendationsChangeListener();
 
@@ -479,7 +504,8 @@
      */
     @MediumTest
     @Test
-    public void testRemovePrintServiceRecommendationsChangeListener() throws Exception {
+    @NoActivity
+    public void testRemovePrintServiceRecommendationsChangeListener() throws Throwable {
         final IRecommendationsChangeListener listener =
                 createMockIPrintServiceRecommendationsChangeListener();
 
@@ -498,7 +524,8 @@
      */
     @MediumTest
     @Test
-    public void testGetPrintServiceRecommendations() throws Exception {
+    @NoActivity
+    public void testGetPrintServiceRecommendations() throws Throwable {
         assertException(() -> mIPrintManager.getPrintServiceRecommendations(mUserId),
                 SecurityException.class);
 
@@ -510,7 +537,8 @@
      */
     @MediumTest
     @Test
-    public void testCreatePrinterDiscoverySession() throws Exception {
+    @NoActivity
+    public void testCreatePrinterDiscoverySession() throws Throwable {
         final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
 
         mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
@@ -533,7 +561,7 @@
      */
     @LargeTest
     @Test
-    public void testStartPrinterDiscovery() throws Exception {
+    public void testStartPrinterDiscovery() throws Throwable {
         startPrinting();
 
         final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
@@ -562,6 +590,8 @@
                 NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -569,7 +599,8 @@
      */
     @MediumTest
     @Test
-    public void testStopPrinterDiscovery() throws Exception {
+    @NoActivity
+    public void testStopPrinterDiscovery() throws Throwable {
         final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
 
         mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
@@ -590,7 +621,7 @@
      */
     @LargeTest
     @Test
-    public void testValidatePrinters() throws Exception {
+    public void testValidatePrinters() throws Throwable {
         startPrinting();
 
         final List<PrinterId> goodPrinters = new ArrayList<>();
@@ -617,6 +648,8 @@
                 NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -624,7 +657,7 @@
      */
     @LargeTest
     @Test
-    public void testStartPrinterStateTracking() throws Exception {
+    public void testStartPrinterStateTracking() throws Throwable {
         startPrinting();
 
         mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -636,6 +669,8 @@
                 NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -643,7 +678,7 @@
      */
     @LargeTest
     @Test
-    public void testGetCustomPrinterIcon() throws Exception {
+    public void testGetCustomPrinterIcon() throws Throwable {
         startPrinting();
 
         mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
@@ -655,6 +690,8 @@
                 NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -662,7 +699,7 @@
      */
     @LargeTest
     @Test
-    public void testStopPrinterStateTracking() throws Exception {
+    public void testStopPrinterStateTracking() throws Throwable {
         startPrinting();
 
         mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -679,6 +716,8 @@
                 NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
+
+        endPrinting();
     }
 
     /**
@@ -686,7 +725,8 @@
      */
     @MediumTest
     @Test
-    public void testDestroyPrinterDiscoverySession() throws Exception {
+    @NoActivity
+    public void testDestroyPrinterDiscoverySession() throws Throwable {
         final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
 
         mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java
deleted file mode 100644
index e9b001f..0000000
--- a/core/tests/coretests/src/android/print/PrintTestActivity.java
+++ /dev/null
@@ -1,32 +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.print;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class PrintTestActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-    }
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
deleted file mode 100644
index 9c11c22..0000000
--- a/core/tests/coretests/src/android/print/mockservice/MockPrintService.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.print.mockservice;
-
-public class MockPrintService extends StubbablePrintService {
-
-    private static final Object sLock = new Object();
-
-    private static PrintServiceCallbacks sCallbacks;
-
-    public static void setCallbacks(PrintServiceCallbacks callbacks) {
-        synchronized (sLock) {
-            sCallbacks = callbacks;
-        }
-    }
-
-    @Override
-    protected PrintServiceCallbacks getCallbacks() {
-        synchronized (sLock) {
-            if (sCallbacks != null) {
-                sCallbacks.setService(this);
-            }
-            return sCallbacks;
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
deleted file mode 100644
index 4e89207..0000000
--- a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
+++ /dev/null
@@ -1,39 +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.print.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-
-public abstract class PrintServiceCallbacks {
-
-    private PrintService mService;
-
-    public PrintService getService() {
-        return mService;
-    }
-
-    public void setService(PrintService service) {
-        mService = service;
-    }
-
-    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
-
-    public abstract void onRequestCancelPrintJob(PrintJob printJob);
-
-    public abstract void onPrintJobQueued(PrintJob printJob);
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
deleted file mode 100644
index be002e2..0000000
--- a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.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.print.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-
-import java.util.List;
-
-public abstract class PrinterDiscoverySessionCallbacks {
-
-    private StubbablePrinterDiscoverySession mSession;
-
-    public void setSession(StubbablePrinterDiscoverySession session) {
-        mSession = session;
-    }
-
-    public StubbablePrinterDiscoverySession getSession() {
-        return mSession;
-    }
-
-    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
-
-    public abstract void onStopPrinterDiscovery();
-
-    public abstract void onValidatePrinters(List<PrinterId> printerIds);
-
-    public abstract void onStartPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
-            CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
-
-    public abstract void onStopPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onDestroy();
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
deleted file mode 100644
index b58b2735..0000000
--- a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
+++ /dev/null
@@ -1,52 +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.print.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-public abstract class StubbablePrintService extends PrintService {
-
-    @Override
-    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            return new StubbablePrinterDiscoverySession(this,
-                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
-        }
-        return null;
-    }
-
-    @Override
-    public void onRequestCancelPrintJob(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onRequestCancelPrintJob(printJob);
-        }
-    }
-
-    @Override
-    public void onPrintJobQueued(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onPrintJobQueued(printJob);
-        }
-    }
-
-    protected abstract PrintServiceCallbacks getCallbacks();
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
deleted file mode 100644
index f3a5373..0000000
--- a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
+++ /dev/null
@@ -1,95 +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.print.mockservice;
-
-import android.support.annotation.NonNull;
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-import java.util.List;
-
-public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
-    private final PrintService mService;
-    private final PrinterDiscoverySessionCallbacks mCallbacks;
-
-    public StubbablePrinterDiscoverySession(PrintService service,
-            PrinterDiscoverySessionCallbacks callbacks) {
-        mService = service;
-        mCallbacks = callbacks;
-        if (mCallbacks != null) {
-            mCallbacks.setSession(this);
-        }
-    }
-
-    public PrintService getService() {
-        return mService;
-    }
-
-    @Override
-    public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterDiscovery(priorityList);
-        }
-    }
-
-    @Override
-    public void onStopPrinterDiscovery() {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterDiscovery();
-        }
-    }
-
-    @Override
-    public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
-        if (mCallbacks != null) {
-            mCallbacks.onValidatePrinters(printerIds);
-        }
-    }
-
-    @Override
-    public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull CustomPrinterIconCallback callback) {
-        if (mCallbacks != null) {
-            mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
-        }
-    }
-
-    @Override
-    public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mCallbacks != null) {
-            mCallbacks.onDestroy();
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6731536..5848649 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -463,7 +463,6 @@
                  Settings.Secure.NFC_PAYMENT_FOREGROUND,
                  Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
                  Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
-                 Settings.Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME,
                  Settings.Secure.PACKAGE_VERIFIER_STATE,
                  Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
                  Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 461d537..4e83221 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -214,7 +214,7 @@
         assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
     }
 
-    /** Test BatteryStatsImpl.noteScreenStateLocked. */
+    /** Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly. */
     @SmallTest
     public void testNoteScreenStateLocked() throws Exception {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
@@ -233,4 +233,52 @@
         assertEquals(bi.getScreenState(), Display.STATE_OFF);
     }
 
+    /** Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly.
+     *
+     *  Unknown and doze should both be subset of off state
+     *
+     *  Timeline 0----100----200----310----400------------1000
+     *  Unknown         -------
+     *  On                     -------
+     *  Off             -------       ----------------------
+     *  Doze                                ----------------
+     */
+    @SmallTest
+    public void testNoteScreenStateTimersLocked() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        clocks.realtime = clocks.uptime = 100;
+        // Device startup, setOnBatteryLocked calls updateTimebases
+        bi.updateTimeBasesLocked(true, Display.STATE_UNKNOWN, 100_000, 100_000);
+        // Turn on display at 200us
+        clocks.realtime = clocks.uptime = 200;
+        bi.noteScreenStateLocked(Display.STATE_ON);
+        assertEquals(150_000, bi.computeBatteryRealtime(250_000, STATS_SINCE_CHARGED));
+        assertEquals(100_000, bi.computeBatteryScreenOffRealtime(250_000, STATS_SINCE_CHARGED));
+        assertEquals(50_000, bi.getScreenOnTime(250_000, STATS_SINCE_CHARGED));
+        assertEquals(0, bi.getScreenDozeTime(250_000, STATS_SINCE_CHARGED));
+
+        clocks.realtime = clocks.uptime = 310;
+        bi.noteScreenStateLocked(Display.STATE_OFF);
+        assertEquals(250_000, bi.computeBatteryRealtime(350_000, STATS_SINCE_CHARGED));
+        assertEquals(140_000, bi.computeBatteryScreenOffRealtime(350_000, STATS_SINCE_CHARGED));
+        assertEquals(110_000, bi.getScreenOnTime(350_000, STATS_SINCE_CHARGED));
+        assertEquals(0, bi.getScreenDozeTime(350_000, STATS_SINCE_CHARGED));
+
+        clocks.realtime = clocks.uptime = 400;
+        bi.noteScreenStateLocked(Display.STATE_DOZE);
+        assertEquals(400_000, bi.computeBatteryRealtime(500_000, STATS_SINCE_CHARGED));
+        assertEquals(290_000, bi.computeBatteryScreenOffRealtime(500_000, STATS_SINCE_CHARGED));
+        assertEquals(110_000, bi.getScreenOnTime(500_000, STATS_SINCE_CHARGED));
+        assertEquals(100_000, bi.getScreenDozeTime(500_000, STATS_SINCE_CHARGED));
+
+        clocks.realtime = clocks.uptime = 1000;
+        bi.noteScreenStateLocked(Display.STATE_OFF);
+        assertEquals(1400_000, bi.computeBatteryRealtime(1500_000, STATS_SINCE_CHARGED));
+        assertEquals(1290_000, bi.computeBatteryScreenOffRealtime(1500_000, STATS_SINCE_CHARGED));
+        assertEquals(110_000, bi.getScreenOnTime(1500_000, STATS_SINCE_CHARGED));
+        assertEquals(600_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED));
+    }
+
 }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 57c7549..0072012 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.WorkerThread;
 import android.content.res.ResourcesImpl;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1233,6 +1234,7 @@
      * @param stream   The outputstream to write the compressed data.
      * @return true if successfully compressed to the specified stream.
      */
+    @WorkerThread
     public boolean compress(CompressFormat format, int quality, OutputStream stream) {
         checkRecycled("Can't compress a recycled bitmap");
         // do explicit check before calling the native method
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index ffb39e3..f5bf754 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -354,6 +354,7 @@
          * decode, in the case of which a more accurate, but slightly slower,
          * IDCT method will be used instead.
          */
+        @Deprecated
         public boolean inPreferQualityOverSpeed;
 
         /**
@@ -412,6 +413,7 @@
          * can check, inbetween the bounds decode and the image decode, to see
          * if the operation is canceled.
          */
+        @Deprecated
         public boolean mCancel;
 
         /**
@@ -426,6 +428,7 @@
          *  or if inJustDecodeBounds is true, will set outWidth/outHeight
          *  to -1
          */
+        @Deprecated
         public void requestCancelDecode() {
             mCancel = true;
         }
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index afb1193..f7a90b0 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -22,10 +22,11 @@
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <log/log.h>
 
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <sys/mman.h>
 
@@ -366,4 +367,4 @@
 }
 
 } /* namespace uirenderer */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index c1ca1e7..1e30d23 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -81,10 +81,10 @@
     mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"),
             gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888);
 
-    SurfaceComposerClient::openGlobalTransaction();
-    mSurfaceControl->setLayer(0x7FFFFFF);
-    mSurfaceControl->show();
-    SurfaceComposerClient::closeGlobalTransaction();
+    SurfaceComposerClient::Transaction t;
+    t.setLayer(mSurfaceControl, 0x7FFFFFF)
+            .show(mSurfaceControl)
+            .apply();
     mSurface = mSurfaceControl->getSurface();
 }
 
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index ed31b12..173cd50 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -148,8 +148,9 @@
         }
     }
 
-    // Resize sprites if needed, inside a global transaction.
-    bool haveGlobalTransaction = false;
+    // Resize sprites if needed.
+    SurfaceComposerClient::Transaction t;
+    bool needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
 
@@ -158,36 +159,24 @@
             int32_t desiredHeight = update.state.icon.bitmap.height();
             if (update.state.surfaceWidth < desiredWidth
                     || update.state.surfaceHeight < desiredHeight) {
-                if (!haveGlobalTransaction) {
-                    SurfaceComposerClient::openGlobalTransaction();
-                    haveGlobalTransaction = true;
-                }
+                needApplyTransaction = true;
 
-                status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
-                if (status) {
-                    ALOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
-                            status, update.state.surfaceWidth, update.state.surfaceHeight,
-                            desiredWidth, desiredHeight);
-                } else {
-                    update.state.surfaceWidth = desiredWidth;
-                    update.state.surfaceHeight = desiredHeight;
-                    update.state.surfaceDrawn = false;
-                    update.surfaceChanged = surfaceChanged = true;
+                t.setSize(update.state.surfaceControl,
+                        desiredWidth, desiredHeight);
+                update.state.surfaceWidth = desiredWidth;
+                update.state.surfaceHeight = desiredHeight;
+                update.state.surfaceDrawn = false;
+                update.surfaceChanged = surfaceChanged = true;
 
-                    if (update.state.surfaceVisible) {
-                        status = update.state.surfaceControl->hide();
-                        if (status) {
-                            ALOGE("Error %d hiding sprite surface after resize.", status);
-                        } else {
-                            update.state.surfaceVisible = false;
-                        }
-                    }
+                if (update.state.surfaceVisible) {
+                    t.hide(update.state.surfaceControl);
+                    update.state.surfaceVisible = false;
                 }
             }
         }
     }
-    if (haveGlobalTransaction) {
-        SurfaceComposerClient::closeGlobalTransaction();
+    if (needApplyTransaction) {
+        t.apply();
     }
 
     // Redraw sprites if needed.
@@ -240,8 +229,7 @@
         }
     }
 
-    // Set sprite surface properties and make them visible.
-    bool haveTransaction = false;
+    needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
 
@@ -253,75 +241,59 @@
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
                         | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
-            status_t status;
-            if (!haveTransaction) {
-                SurfaceComposerClient::openGlobalTransaction();
-                haveTransaction = true;
-            }
+            needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
-                status = update.state.surfaceControl->setAlpha(update.state.alpha);
-                if (status) {
-                    ALOGE("Error %d setting sprite surface alpha.", status);
-                }
+                t.setAlpha(update.state.surfaceControl,
+                        update.state.alpha);
             }
 
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & (DIRTY_POSITION
                             | DIRTY_HOTSPOT)))) {
-                status = update.state.surfaceControl->setPosition(
+                t.setPosition(
+                        update.state.surfaceControl,
                         update.state.positionX - update.state.icon.hotSpotX,
                         update.state.positionY - update.state.icon.hotSpotY);
-                if (status) {
-                    ALOGE("Error %d setting sprite surface position.", status);
-                }
             }
 
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible
                             || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
-                status = update.state.surfaceControl->setMatrix(
+                t.setMatrix(
+                        update.state.surfaceControl,
                         update.state.transformationMatrix.dsdx,
                         update.state.transformationMatrix.dtdx,
                         update.state.transformationMatrix.dsdy,
                         update.state.transformationMatrix.dtdy);
-                if (status) {
-                    ALOGE("Error %d setting sprite surface transformation matrix.", status);
-                }
             }
 
             int32_t surfaceLayer = mOverlayLayer + update.state.layer;
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
-                status = update.state.surfaceControl->setLayer(surfaceLayer);
-                if (status) {
-                    ALOGE("Error %d setting sprite surface layer.", status);
-                }
+                t.setLayer(update.state.surfaceControl, surfaceLayer);
             }
 
             if (becomingVisible) {
-                status = update.state.surfaceControl->show();
-                if (status) {
-                    ALOGE("Error %d showing sprite surface.", status);
-                } else {
-                    update.state.surfaceVisible = true;
-                    update.surfaceChanged = surfaceChanged = true;
-                }
+                t.show(update.state.surfaceControl);
+
+                update.state.surfaceVisible = true;
+                update.surfaceChanged = surfaceChanged = true;
             } else if (becomingHidden) {
-                status = update.state.surfaceControl->hide();
-                if (status) {
-                    ALOGE("Error %d hiding sprite surface.", status);
-                } else {
-                    update.state.surfaceVisible = false;
-                    update.surfaceChanged = surfaceChanged = true;
-                }
+                t.hide(update.state.surfaceControl);
+
+                update.state.surfaceVisible = false;
+                update.surfaceChanged = surfaceChanged = true;
             }
         }
     }
 
-    if (haveTransaction) {
-        SurfaceComposerClient::closeGlobalTransaction();
+    if (needApplyTransaction) {
+        status_t status = t.apply();
+        if (status) {
+            ALOGE("Error applying Surface transaction");
+        }
     }
 
     // If any surfaces were changed, write back the new surface properties to the sprites.
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
index a534816..2a2b087 100644
--- a/libs/protoutil/Android.mk
+++ b/libs/protoutil/Android.mk
@@ -22,15 +22,15 @@
         -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
 
 LOCAL_SHARED_LIBRARIES := \
-        libbinder \
+        libcutils \
         liblog \
-        libutils
 
 LOCAL_C_INCLUDES := \
         $(LOCAL_PATH)/include
 
 LOCAL_SRC_FILES := \
         src/EncodedBuffer.cpp \
+        src/ProtoOutputStream.cpp \
         src/protobuf.cpp \
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index cf09609..e568e4c 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -52,10 +52,10 @@
         size_t index() const;
         size_t offset() const;
 
-        void move(size_t amt);
-        inline void move() { move(1); };
+        Pointer* move(size_t amt);
+        inline Pointer* move() { return move(1); };
+        Pointer* rewind();
 
-        void rewind();
         Pointer copy() const;
 
     private:
@@ -88,15 +88,71 @@
     size_t currentToWrite();
 
     /**
-     * Write a varint into a vector. Return the size of the varint.
+     * Write a single byte to the buffer.
      */
-    size_t writeRawVarint(uint32_t val);
+    void writeRawByte(uint8_t val);
+
+    /**
+     * Write a varint32 into the buffer. Return the size of the varint.
+     */
+    size_t writeRawVarint32(uint32_t val);
+
+    /**
+     * Write a varint64 into the buffer. Return the size of the varint.
+     */
+    size_t writeRawVarint64(uint64_t val);
+
+    /**
+     * Write Fixed32 into the buffer.
+     */
+    void writeRawFixed32(uint32_t val);
+
+    /**
+     * Write Fixed64 into the buffer.
+     */
+    void writeRawFixed64(uint64_t val);
 
     /**
      * Write a protobuf header. Return the size of the header.
      */
     size_t writeHeader(uint32_t fieldId, uint8_t wireType);
 
+    /********************************* Edit APIs ************************************************/
+    /**
+     * Returns the edit pointer.
+     */
+    Pointer* ep();
+
+    /**
+     * Read a single byte at ep, and move ep to next byte;
+     */
+    uint8_t readRawByte();
+
+    /**
+     * Read varint starting at ep, ep will move to pos of next byte.
+     */
+    uint64_t readRawVarint();
+
+    /**
+     * Read 4 bytes starting at ep, ep will move to pos of next byte.
+     */
+    uint32_t readRawFixed32();
+
+    /**
+     * Read 8 bytes starting at ep, ep will move to pos of next byte.
+     */
+    uint64_t readRawFixed64();
+
+    /**
+     * Edit 4 bytes starting at pos.
+     */
+    void editRawFixed32(size_t pos, uint32_t val);
+
+    /**
+     * Copy _size_ bytes of data starting at __srcPos__ to wp.
+     */
+    void copy(size_t srcPos, size_t size);
+
     /********************************* Read APIs ************************************************/
     class iterator;
     friend class iterator;
@@ -141,9 +197,8 @@
 
         /**
          * Read varint from iterator, the iterator will point to next available byte.
-         * Return the number of bytes of the varint.
          */
-        uint32_t readRawVarint();
+        uint64_t readRawVarint();
 
     private:
         const EncodedBuffer& mData;
@@ -160,6 +215,7 @@
     vector<uint8_t*> mBuffers;
 
     Pointer mWp;
+    Pointer mEp;
 
     inline uint8_t* at(const Pointer& p) const; // helper function to get value
 };
@@ -167,4 +223,5 @@
 } // util
 } // android
 
-#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file
+#endif // ANDROID_UTIL_ENCODED_BUFFER_H
+
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
new file mode 100644
index 0000000..49ec169
--- /dev/null
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -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.
+ */
+
+#ifndef ANDROID_UTIL_PROTOOUTPUT_STREAM_H
+#define ANDROID_UTIL_PROTOOUTPUT_STREAM_H
+
+#include <android/util/EncodedBuffer.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace android {
+namespace util {
+
+/**
+ * Class to write to a protobuf stream.
+ *
+ * Each write method takes an ID code from the protoc generated classes
+ * and the value to write.  To make a nested object, call start
+ * and then end when you are done.
+ *
+ * See the java version implementation (ProtoOutputStream.java) for more infos.
+ */
+class ProtoOutputStream
+{
+public:
+    ProtoOutputStream(int fd);
+    ~ProtoOutputStream();
+
+    /**
+     * Write APIs for dumping protobuf data. Returns true if the write succeeds.
+     */
+    bool write(uint64_t fieldId, double val);
+    bool write(uint64_t fieldId, float val);
+    bool write(uint64_t fieldId, int val);
+    bool write(uint64_t fieldId, long long val);
+    bool write(uint64_t fieldId, bool val);
+    bool write(uint64_t fieldId, std::string val);
+    bool write(uint64_t fieldId, const char* val);
+
+    /**
+     * Starts a sub-message write session.
+     * Returns a token of this write session.
+     * Must call end(token) when finish write this sub-message.
+     */
+    long long start(uint64_t fieldId);
+    void end(long long token);
+
+    /**
+     * Flushes the protobuf data out.
+     */
+    bool flush();
+
+private:
+    EncodedBuffer mBuffer;
+    int mFd;
+    size_t mCopyBegin;
+    bool mCompact;
+    int mDepth;
+    int mObjectId;
+    long long mExpectedObjectToken;
+
+    inline void writeDoubleImpl(uint32_t id, double val);
+    inline void writeFloatImpl(uint32_t id, float val);
+    inline void writeInt64Impl(uint32_t id, long long val);
+    inline void writeInt32Impl(uint32_t id, int val);
+    inline void writeUint64Impl(uint32_t id, uint64_t val);
+    inline void writeUint32Impl(uint32_t id, uint32_t val);
+    inline void writeFixed64Impl(uint32_t id, uint64_t val);
+    inline void writeFixed32Impl(uint32_t id, uint32_t val);
+    inline void writeSFixed64Impl(uint32_t id, long long val);
+    inline void writeSFixed32Impl(uint32_t id, int val);
+    inline void writeZigzagInt64Impl(uint32_t id, long long val);
+    inline void writeZigzagInt32Impl(uint32_t id, int val);
+    inline void writeEnumImpl(uint32_t id, int val);
+    inline void writeBoolImpl(uint32_t id, bool val);
+    inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size);
+
+    bool compact();
+    size_t editEncodedSize(size_t rawSize);
+    bool compactSize(size_t rawSize);
+};
+
+}
+}
+
+#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H
\ No newline at end of file
diff --git a/libs/protoutil/include/android/util/protobuf.h b/libs/protoutil/include/android/util/protobuf.h
index f4e8d09..ca45e26 100644
--- a/libs/protoutil/include/android/util/protobuf.h
+++ b/libs/protoutil/include/android/util/protobuf.h
@@ -24,6 +24,9 @@
 
 using namespace std;
 
+const int FIELD_ID_SHIFT = 3;
+const uint8_t WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1;
+
 const uint8_t WIRE_TYPE_VARINT = 0;
 const uint8_t WIRE_TYPE_FIXED64 = 1;
 const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2;
@@ -35,16 +38,20 @@
 uint8_t read_wire_type(uint32_t varint);
 
 /**
- * read field id from varint, it is varint >> 3;
+ * Read field id from varint, it is varint >> 3;
  */
 uint32_t read_field_id(uint32_t varint);
 
 /**
- * Write a varint into the buffer. Return the next position to write at.
- * There must be 10 bytes in the buffer. The same as
- * EncodedBuffer.writeRawVarint32
+ * Get the size of a varint.
  */
-uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
+size_t get_varint_size(uint64_t varint);
+
+/**
+ * Write a varint into the buffer. Return the next position to write at.
+ * There must be 10 bytes in the buffer.
+ */
+uint8_t* write_raw_varint(uint8_t* buf, uint64_t val);
 
 /**
  * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 84dc5b6..435ae88 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android/util/EncodedBuffer.h>
+#include <android/util/protobuf.h>
 
 #include <stdlib.h>
 
@@ -52,19 +53,21 @@
     return mOffset;
 }
 
-void
+EncodedBuffer::Pointer*
 EncodedBuffer::Pointer::move(size_t amt)
 {
     size_t newOffset = mOffset + amt;
     mIndex += newOffset / mChunkSize;
     mOffset = newOffset % mChunkSize;
+    return this;
 }
 
-void
+EncodedBuffer::Pointer*
 EncodedBuffer::Pointer::rewind()
 {
     mIndex = 0;
     mOffset = 0;
+    return this;
 }
 
 EncodedBuffer::Pointer
@@ -86,6 +89,7 @@
 {
     mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
     mWp = Pointer(mChunkSize);
+    mEp = Pointer(mChunkSize);
 }
 
 EncodedBuffer::~EncodedBuffer()
@@ -137,28 +141,136 @@
     return mChunkSize - mWp.offset();
 }
 
+void
+EncodedBuffer::writeRawByte(uint8_t val)
+{
+    *writeBuffer() = val;
+    mWp.move();
+}
+
 size_t
-EncodedBuffer::writeRawVarint(uint32_t val)
+EncodedBuffer::writeRawVarint64(uint64_t val)
 {
     size_t size = 0;
     while (true) {
         size++;
         if ((val & ~0x7F) == 0) {
-            *writeBuffer() = (uint8_t) val;
-            mWp.move();
+            writeRawByte((uint8_t) val);
             return size;
         } else {
-            *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80);
-            mWp.move();
+            writeRawByte((uint8_t)((val & 0x7F) | 0x80));
             val >>= 7;
         }
     }
 }
 
 size_t
+EncodedBuffer::writeRawVarint32(uint32_t val)
+{
+    uint64_t v =(uint64_t)val;
+    return writeRawVarint64(v);
+}
+
+void
+EncodedBuffer::writeRawFixed32(uint32_t val)
+{
+    writeRawByte((uint8_t) val);
+    writeRawByte((uint8_t) (val>>8));
+    writeRawByte((uint8_t) (val>>16));
+    writeRawByte((uint8_t) (val>>24));
+}
+
+void
+EncodedBuffer::writeRawFixed64(uint64_t val)
+{
+    writeRawByte((uint8_t) val);
+    writeRawByte((uint8_t) (val>>8));
+    writeRawByte((uint8_t) (val>>16));
+    writeRawByte((uint8_t) (val>>24));
+    writeRawByte((uint8_t) (val>>32));
+    writeRawByte((uint8_t) (val>>40));
+    writeRawByte((uint8_t) (val>>48));
+    writeRawByte((uint8_t) (val>>56));
+}
+
+size_t
 EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType)
 {
-    return writeRawVarint((fieldId << 3) | wireType);
+    return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType);
+}
+
+/******************************** Edit APIs ************************************************/
+EncodedBuffer::Pointer*
+EncodedBuffer::ep()
+{
+    return &mEp;
+}
+
+uint8_t
+EncodedBuffer::readRawByte()
+{
+    uint8_t val = *at(mEp);
+    mEp.move();
+    return val;
+}
+
+uint64_t
+EncodedBuffer::readRawVarint()
+{
+    uint64_t val = 0, shift = 0;
+    size_t start = mEp.pos();
+    while (true) {
+        uint8_t byte = readRawByte();
+        val += (byte & 0x7F) << shift;
+        if ((byte & 0x80) == 0) break;
+        shift += 7;
+    }
+    return val;
+}
+
+uint32_t
+EncodedBuffer::readRawFixed32()
+{
+    uint32_t val = 0;
+    for (auto i=0; i<32; i+=8) {
+        val += (uint32_t)readRawByte() << i;
+    }
+    return val;
+}
+
+uint64_t
+EncodedBuffer::readRawFixed64()
+{
+    uint64_t val = 0;
+    for (auto i=0; i<64; i+=8) {
+        val += (uint64_t)readRawByte() << i;
+    }
+    return val;
+}
+
+void
+EncodedBuffer::editRawFixed32(size_t pos, uint32_t val)
+{
+    size_t oldPos = mEp.pos();
+    mEp.rewind()->move(pos);
+    for (auto i=0; i<32; i+=8) {
+        *at(mEp) = (uint8_t) (val >> i);
+        mEp.move();
+    }
+    mEp.rewind()->move(oldPos);
+}
+
+void
+EncodedBuffer::copy(size_t srcPos, size_t size)
+{
+    if (size == 0) return;
+    Pointer cp(mChunkSize);
+    cp.move(srcPos);
+
+    while (cp.pos() < srcPos + size) {
+        writeRawByte(*at(cp));
+        cp.move();
+    }
 }
 
 /********************************* Read APIs ************************************************/
@@ -220,10 +332,10 @@
     return res;
 }
 
-uint32_t
+uint64_t
 EncodedBuffer::iterator::readRawVarint()
 {
-    uint32_t val = 0, shift = 0;
+    uint64_t val = 0, shift = 0;
     while (true) {
         uint8_t byte = next();
         val += (byte & 0x7F) << shift;
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
new file mode 100644
index 0000000..e9ca0dc
--- /dev/null
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -0,0 +1,652 @@
+/*
+ * 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 "libprotoutil"
+
+#include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
+#include <cutils/log.h>
+#include <cstring>
+
+namespace android {
+namespace util {
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+/**
+ * Mask for the field types stored in a fieldId.  Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
+
+const uint64_t FIELD_TYPE_UNKNOWN  = 0;
+const uint64_t TYPE_DOUBLE         = 1ULL << FIELD_TYPE_SHIFT;   // double, exactly eight bytes on the wire.
+const uint64_t TYPE_FLOAT          = 2ULL << FIELD_TYPE_SHIFT;   // float, exactly four bytes on the wire.
+const uint64_t TYPE_INT64          = 3ULL << FIELD_TYPE_SHIFT;   // int64, varint on the wire.  Negative numbers
+                                                                 // take 10 bytes.  Use TYPE_SINT64 if negative
+                                                                 // values are likely.
+const uint64_t TYPE_UINT64         = 4ULL << FIELD_TYPE_SHIFT;   // uint64, varint on the wire.
+const uint64_t TYPE_INT32          = 5ULL << FIELD_TYPE_SHIFT;   // int32, varint on the wire.  Negative numbers
+                                                                 // take 10 bytes.  Use TYPE_SINT32 if negative
+                                                                 // values are likely.
+const uint64_t TYPE_FIXED64        = 6ULL << FIELD_TYPE_SHIFT;   // uint64, exactly eight bytes on the wire.
+const uint64_t TYPE_FIXED32        = 7ULL << FIELD_TYPE_SHIFT;   // uint32, exactly four bytes on the wire.
+const uint64_t TYPE_BOOL           = 8ULL << FIELD_TYPE_SHIFT;   // bool, varint on the wire.
+const uint64_t TYPE_STRING         = 9ULL << FIELD_TYPE_SHIFT;   // UTF-8 text.
+const uint64_t TYPE_GROUP          = 10ULL << FIELD_TYPE_SHIFT;  // Tag-delimited message.  Deprecated.
+const uint64_t TYPE_MESSAGE        = 11ULL << FIELD_TYPE_SHIFT;  // Length-delimited message.
+
+const uint64_t TYPE_BYTES          = 12ULL << FIELD_TYPE_SHIFT;  // Arbitrary byte array.
+const uint64_t TYPE_UINT32         = 13ULL << FIELD_TYPE_SHIFT;  // uint32, varint on the wire
+const uint64_t TYPE_ENUM           = 14ULL << FIELD_TYPE_SHIFT;  // Enum, varint on the wire
+const uint64_t TYPE_SFIXED32       = 15ULL << FIELD_TYPE_SHIFT;  // int32, exactly four bytes on the wire
+const uint64_t TYPE_SFIXED64       = 16ULL << FIELD_TYPE_SHIFT;  // int64, exactly eight bytes on the wire
+const uint64_t TYPE_SINT32         = 17ULL << FIELD_TYPE_SHIFT;  // int32, ZigZag-encoded varint on the wire
+const uint64_t TYPE_SINT64         = 18ULL << FIELD_TYPE_SHIFT;  // int64, ZigZag-encoded varint on the wire
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+ProtoOutputStream::ProtoOutputStream(int fd)
+        :mBuffer(),
+         mFd(fd),
+         mCopyBegin(0),
+         mCompact(false),
+         mDepth(0),
+         mObjectId(0),
+         mExpectedObjectToken(0LL)
+{
+}
+
+ProtoOutputStream::~ProtoOutputStream()
+{
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, double val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
+        case TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
+        case TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
+        case TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
+        case TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
+        case TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
+        case TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
+        case TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
+        case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
+        case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
+        case TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
+        case TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
+        default:
+            ALOGW("Field type %d is not supported when writing double val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+    return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, float val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
+        case TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
+        case TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
+        case TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
+        case TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
+        case TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
+        case TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
+        case TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
+        case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
+        case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
+        case TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
+        case TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
+        default:
+            ALOGW("Field type %d is not supported when writing float val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+    return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, int val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
+        case TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
+        case TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
+        case TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
+        case TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
+        case TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
+        case TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
+        case TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
+        case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
+        case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
+        case TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
+        case TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
+        case TYPE_ENUM:     writeEnumImpl(id, (int)val);                break;
+        case TYPE_BOOL:     writeBoolImpl(id, val != 0);                break;
+        default:
+            ALOGW("Field type %d is not supported when writing int val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+    return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, long long val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
+        case TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
+        case TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
+        case TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
+        case TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
+        case TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
+        case TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
+        case TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
+        case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
+        case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
+        case TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
+        case TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
+        case TYPE_ENUM:     writeEnumImpl(id, (int)val);                break;
+        case TYPE_BOOL:     writeBoolImpl(id, val != 0);                break;
+        default:
+            ALOGW("Field type %d is not supported when writing long long val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+    return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, bool val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_BOOL:
+            writeBoolImpl(id, val);
+            return true;
+        default:
+            ALOGW("Field type %d is not supported when writing bool val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, string val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_STRING:
+            writeUtf8StringImpl(id, val.c_str(), val.size());
+            return true;
+        default:
+            ALOGW("Field type %d is not supported when writing string val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, const char* val)
+{
+    if (mCompact) return false;
+    const uint32_t id = (uint32_t)fieldId;
+    int size = 0;
+    while (val[size] != '\0') size++;
+    switch (fieldId & FIELD_TYPE_MASK) {
+        case TYPE_STRING:
+            writeUtf8StringImpl(id, val, size);
+            return true;
+        default:
+            ALOGW("Field type %d is not supported when writing char[] val.",
+                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            return false;
+    }
+}
+
+/**
+ * Make a token.
+ *  Bits 61-63 - tag size (So we can go backwards later if the object had not data)
+ *                - 3 bits, max value 7, max value needed 5
+ *  Bit  60    - true if the object is repeated
+ *  Bits 59-51 - depth (For error checking)
+ *                - 9 bits, max value 512, when checking, value is masked (if we really
+ *                  are more than 512 levels deep)
+ *  Bits 32-50 - objectId (For error checking)
+ *                - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
+ *                  because of the overflow, and only the tokens are compared.
+ *  Bits  0-31 - offset of the first size field in the buffer.
+ */
+long long
+makeToken(int tagSize, bool repeated, int depth, int objectId, int sizePos) {
+    return ((0x07L & (long long)tagSize) << 61)
+            | (repeated ? (1LL << 60) : 0)
+            | (0x01ffL & (long long)depth) << 51
+            | (0x07ffffL & (long long)objectId) << 32
+            | (0x0ffffffffL & (long long)sizePos);
+}
+
+/**
+ * Get the encoded tag size from the token.
+ */
+static int getTagSizeFromToken(long long token) {
+    return (int)(0x7 & (token >> 61));
+}
+
+/**
+ * Get the nesting depth of startObject calls from the token.
+ */
+static int getDepthFromToken(long long token) {
+    return (int)(0x01ff & (token >> 51));
+}
+
+/**
+ * Get the location of the childRawSize (the first 32 bit size field) in this object.
+ */
+static int getSizePosFromToken(long long token) {
+    return (int)token;
+}
+
+long long
+ProtoOutputStream::start(uint64_t fieldId)
+{
+    if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
+        ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
+        return 0;
+    }
+
+    uint32_t id = (uint32_t)fieldId;
+    mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
+
+    size_t sizePos = mBuffer.wp()->pos();
+
+    mDepth++;
+    mObjectId++;
+    mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
+
+    mExpectedObjectToken = makeToken(get_varint_size(id),
+        (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
+    return mExpectedObjectToken;
+}
+
+void
+ProtoOutputStream::end(long long token)
+{
+    if (token != mExpectedObjectToken) {
+        ALOGE("Unexpected token: 0x%llx, should be 0x%llx", token, mExpectedObjectToken);
+        return;
+    }
+
+    int depth = getDepthFromToken(token);
+    if (depth != (mDepth & 0x01ff)) {
+        ALOGE("Unexpected depth: %d, should be %d", depth, mDepth);
+        return;
+    }
+    mDepth--;
+
+    int sizePos = getSizePosFromToken(token);
+    // number of bytes written in this start-end session.
+    int childRawSize = mBuffer.wp()->pos() - sizePos - 8;
+
+    // retrieve the old token from stack.
+    mBuffer.ep()->rewind()->move(sizePos);
+    mExpectedObjectToken = mBuffer.readRawFixed64();
+
+    // If raw size is larger than 0, write the negative value here to indicate a compact is needed.
+    if (childRawSize > 0) {
+        mBuffer.editRawFixed32(sizePos, -childRawSize);
+        mBuffer.editRawFixed32(sizePos+4, -1);
+    } else {
+        // reset wp which erase the header tag of the message when its size is 0.
+        mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token));
+    }
+}
+
+bool
+ProtoOutputStream::compact() {
+    if (mCompact) return true;
+    if (mDepth != 0) {
+        ALOGE("Can't compact when depth(%d) is not zero. Missing calls to end.", mDepth);
+        return false;
+    }
+    // record the size of the original buffer.
+    size_t rawBufferSize = mBuffer.size();
+    if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty;
+
+    // reset edit pointer and recursively compute encoded size of messages.
+    mBuffer.ep()->rewind();
+    if (editEncodedSize(rawBufferSize) == 0) {
+        ALOGE("Failed to editEncodedSize.");
+        return false;
+    }
+
+    // reset both edit pointer and write pointer, and compact recursively.
+    mBuffer.ep()->rewind();
+    mBuffer.wp()->rewind();
+    if (!compactSize(rawBufferSize)) {
+        ALOGE("Failed to compactSize.");
+        return false;
+    }
+    // copy the reset to the buffer.
+    if (mCopyBegin < rawBufferSize) {
+        mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin);
+    }
+
+    // mark true means it is not legal to write to this ProtoOutputStream anymore
+    mCompact = true;
+    return true;
+}
+
+/**
+ * First compaction pass.  Iterate through the data, and fill in the
+ * nested object sizes so the next pass can compact them.
+ */
+size_t
+ProtoOutputStream::editEncodedSize(size_t rawSize)
+{
+    size_t objectStart = mBuffer.ep()->pos();
+    size_t objectEnd = objectStart + rawSize;
+    size_t encodedSize = 0;
+    int childRawSize, childEncodedSize;
+    size_t childEncodedSizePos;
+
+    while (mBuffer.ep()->pos() < objectEnd) {
+        uint32_t tag = (uint32_t)mBuffer.readRawVarint();
+        encodedSize += get_varint_size(tag);
+        switch (read_wire_type(tag)) {
+            case WIRE_TYPE_VARINT:
+                do {
+                    encodedSize++;
+                } while ((mBuffer.readRawByte() & 0x80) != 0);
+                break;
+            case WIRE_TYPE_FIXED64:
+                encodedSize += 8;
+                mBuffer.ep()->move(8);
+                break;
+            case WIRE_TYPE_LENGTH_DELIMITED:
+                childRawSize = (int)mBuffer.readRawFixed32();
+                childEncodedSizePos = mBuffer.ep()->pos();
+                childEncodedSize = (int)mBuffer.readRawFixed32();
+                if (childRawSize >= 0 && childRawSize == childEncodedSize) {
+                    mBuffer.ep()->move(childRawSize);
+                } else if (childRawSize < 0 && childEncodedSize == -1){
+                    childEncodedSize = editEncodedSize(-childRawSize);
+                    mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize);
+                } else {
+                    ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu",
+                            childRawSize, childEncodedSize, childEncodedSizePos);
+                    return 0;
+                }
+                encodedSize += get_varint_size(childEncodedSize) + childEncodedSize;
+                break;
+            case WIRE_TYPE_FIXED32:
+                encodedSize += 4;
+                mBuffer.ep()->move(4);
+                break;
+            default:
+                ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]",
+                        read_wire_type(tag), objectStart, objectEnd);
+                return 0;
+        }
+    }
+    return encodedSize;
+}
+
+/**
+ * Second compaction pass.  Iterate through the data, and copy the data
+ * forward in the buffer, converting the pairs of uint32s into a single
+ * unsigned varint of the size.
+ */
+bool
+ProtoOutputStream::compactSize(size_t rawSize)
+{
+    size_t objectStart = mBuffer.ep()->pos();
+    size_t objectEnd = objectStart + rawSize;
+    int childRawSize, childEncodedSize;
+
+    while (mBuffer.ep()->pos() < objectEnd) {
+        uint32_t tag = (uint32_t)mBuffer.readRawVarint();
+        switch (read_wire_type(tag)) {
+            case WIRE_TYPE_VARINT:
+                while ((mBuffer.readRawByte() & 0x80) != 0) {}
+                break;
+            case WIRE_TYPE_FIXED64:
+                mBuffer.ep()->move(8);
+                break;
+            case WIRE_TYPE_LENGTH_DELIMITED:
+                mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin);
+
+                childRawSize = (int)mBuffer.readRawFixed32();
+                childEncodedSize = (int)mBuffer.readRawFixed32();
+                mCopyBegin = mBuffer.ep()->pos();
+
+                // write encoded size to buffer.
+                mBuffer.writeRawVarint32(childEncodedSize);
+                if (childRawSize >= 0 && childRawSize == childEncodedSize) {
+                    mBuffer.ep()->move(childEncodedSize);
+                } else if (childRawSize < 0){
+                    if (!compactSize(-childRawSize)) return false;
+                } else {
+                    ALOGE("Bad raw or encoded values: raw=%d, encoded=%d",
+                            childRawSize, childEncodedSize);
+                    return false;
+                }
+                break;
+            case WIRE_TYPE_FIXED32:
+                mBuffer.ep()->move(4);
+                break;
+            default:
+                ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]",
+                        read_wire_type(tag), objectStart, objectEnd);
+                return false;
+        }
+    }
+    return true;
+}
+
+static bool write_all(int fd, uint8_t const* buf, size_t size)
+{
+    while (size > 0) {
+        ssize_t amt = ::write(fd, buf, size);
+        if (amt < 0) {
+            return false;
+        }
+        size -= amt;
+        buf += amt;
+    }
+    return true;
+}
+
+bool
+ProtoOutputStream::flush()
+{
+    if (mFd < 0) return false;
+    if (!compact()) return false;
+
+    EncodedBuffer::iterator it = mBuffer.begin();
+    while (it.readBuffer() != NULL) {
+        if (!write_all(mFd, it.readBuffer(), it.currentToRead())) return false;
+        it.rp()->move(it.currentToRead());
+    }
+    return true;
+}
+
+
+// =========================================================================
+// Private functions
+
+/**
+ * bit_cast
+ */
+template <class From, class To>
+inline To bit_cast(From const &from) {
+    To to;
+    memcpy(&to, &from, sizeof(to));
+    return to;
+}
+
+inline void
+ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
+{
+    if (val == 0.0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+    mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
+}
+
+inline void
+ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
+{
+    if (val == 0.0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+    mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
+}
+
+inline void
+ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint64((uint64_t)val);
+}
+
+inline void
+ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint32((uint32_t)val);
+}
+
+inline void
+ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
+{
+   if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint64(val);
+}
+
+inline void
+ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint32(val);
+}
+
+inline void
+ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+    mBuffer.writeRawFixed64(val);
+}
+
+inline void
+ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+    mBuffer.writeRawFixed32(val);
+}
+
+inline void
+ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+    mBuffer.writeRawFixed64((uint64_t)val);
+}
+
+inline void
+ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+    mBuffer.writeRawFixed32((uint32_t)val);
+}
+
+inline void
+ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
+}
+
+inline void
+ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
+{
+    if (val == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
+}
+
+inline void
+ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
+{
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint32((uint32_t) val);
+}
+
+inline void
+ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
+{
+    if (!val) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+    mBuffer.writeRawVarint32(val ? 1 : 0);
+}
+
+inline void
+ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
+{
+    if (val == NULL || size == 0) return;
+    mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
+    mBuffer.writeRawFixed32(size);
+    mBuffer.writeRawFixed32(size);
+    for (size_t i=0; i<size; i++) {
+        mBuffer.writeRawByte((uint8_t)val[i]);
+    }
+}
+
+} // util
+} // android
+
diff --git a/libs/protoutil/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp
index ec5325c..1c7eef9 100644
--- a/libs/protoutil/src/protobuf.cpp
+++ b/libs/protoutil/src/protobuf.cpp
@@ -22,17 +22,28 @@
 uint8_t
 read_wire_type(uint32_t varint)
 {
-    return (uint8_t) (varint & 0x07);
+    return (uint8_t) (varint & WIRE_TYPE_MASK);
 }
 
 uint32_t
 read_field_id(uint32_t varint)
 {
-    return varint >> 3;
+    return varint >> FIELD_ID_SHIFT;
+}
+
+size_t
+get_varint_size(uint64_t varint)
+{
+    size_t size = 1;
+    while ((varint & ~0x7F)) {
+        size++;
+        varint >>= 7;
+    }
+    return size;
 }
 
 uint8_t*
-write_raw_varint(uint8_t* buf, uint32_t val)
+write_raw_varint(uint8_t* buf, uint64_t val)
 {
     uint8_t* p = buf;
     while (true) {
@@ -49,7 +60,7 @@
 uint8_t*
 write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
 {
-    buf = write_raw_varint(buf, (fieldId << 3) | 2);
+    buf = write_raw_varint(buf, (fieldId << FIELD_ID_SHIFT) | WIRE_TYPE_LENGTH_DELIMITED);
     buf = write_raw_varint(buf, size);
     return buf;
 }
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 26ead3d..20405d3 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -202,6 +202,22 @@
      * @see #SUPPRESSIBLE_USAGES
      */
     public final static int SUPPRESSIBLE_NEVER = 3;
+    /**
+     * @hide
+     * Denotes a usage for alarms,
+     * will be muted when the Zen mode doesn't allow alarms
+     * @see #SUPPRESSIBLE_USAGES
+     */
+    public final static int SUPPRESSIBLE_ALARM = 4;
+    /**
+     * @hide
+     * Denotes a usage for all other sounds not caught in SUPPRESSIBLE_NOTIFICATION,
+     * SUPPRESSIBLE_CALL,SUPPRESSIBLE_NEVER or SUPPRESSIBLE_ALARM.
+     * This includes media, system, game, navigation, the assistant, and more.
+     * These will be muted when the Zen mode doesn't allow media/system/other.
+     * @see #SUPPRESSIBLE_USAGES
+     */
+    public final static int SUPPRESSIBLE_MEDIA_SYSTEM_OTHER = 5;
 
     /**
      * @hide
@@ -221,6 +237,13 @@
         SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT,                SUPPRESSIBLE_NOTIFICATION);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_ACCESSIBILITY,          SUPPRESSIBLE_NEVER);
         SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION,               SUPPRESSIBLE_NEVER);
+        SUPPRESSIBLE_USAGES.put(USAGE_ALARM,                             SUPPRESSIBLE_ALARM);
+        SUPPRESSIBLE_USAGES.put(USAGE_MEDIA,                             SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
     }
 
     /**
diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl
index 96223ba..94c1013 100644
--- a/media/java/android/media/tv/ITvInputHardware.aidl
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -40,12 +40,6 @@
     void setStreamVolume(float volume);
 
     /**
-     * Dispatch key event to HDMI service. The events would be automatically converted to
-     * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
-     */
-    boolean dispatchKeyEventToHdmi(in KeyEvent event);
-
-    /**
      * Override default audio sink from audio policy. When override is on, it is
      * TvInputService's responsibility to adjust to audio configuration change
      * (for example, when the audio sink becomes unavailable or more desirable
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d7a9ede..fd1f2cf 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2590,12 +2590,9 @@
             }
         }
 
+        /** @removed */
         public boolean dispatchKeyEventToHdmi(KeyEvent event) {
-            try {
-                return mInterface.dispatchKeyEventToHdmi(event);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+            return false;
         }
 
         public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 7f6980d..28827e6 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -24,7 +24,7 @@
 #include <media/IMediaHTTPService.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/MediaAnalyticsItem.h>
-#include <media/stagefright/Utils.h>            // for FOURCC definition
+#include <media/stagefright/foundation/ByteUtils.h>  // for FOURCC definition
 #include <stdio.h>
 #include <assert.h>
 #include <limits.h>
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
index 3c02453..149be74 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.mk
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4 print-test-util-lib
 
 LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 4a05f6f..307cc93 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -21,10 +21,12 @@
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name=".PrintTestActivity"/>
+        <activity
+            android:name="android.print.test.PrintDocumentActivity"
+            android:theme="@style/NoAnimation" />
 
         <service
-                android:name=".mockservice.MockPrintService"
+                android:name="android.print.test.services.FirstPrintService"
                 android:permission="android.permission.BIND_PRINT_SERVICE">
 
             <intent-filter>
@@ -37,13 +39,15 @@
         </service>
 
         <activity
-                android:name=".mockservice.SettingsActivity"
+                android:name="android.print.test.services.SettingsActivity"
                 android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+                android:theme="@style/NoAnimation"
                 android:exported="true">
         </activity>
 
         <activity
-                android:name=".mockservice.AddPrintersActivity"
+                android:name="android.print.test.services.AddPrintersActivity"
+                android:theme="@style/NoAnimation"
                 android:exported="true">
         </activity>
 
diff --git a/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml
new file mode 100644
index 0000000..49eb257
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+<resources>
+    <style name="NoAnimation" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+</resources>
diff --git a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
index 9eecf45..a6282b1 100644
--- a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
+++ b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
@@ -17,4 +17,4 @@
   -->
 
 <print-service  xmlns:android="http://schemas.android.com/apk/res/android"
-        android:addPrintersActivity="com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity" />
+        android:addPrintersActivity="android.print.test.services.AddPrintersActivity" />
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
deleted file mode 100644
index 9a7f362..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
+++ /dev/null
@@ -1,275 +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.printspooler.outofprocess.tests;
-
-import static android.content.pm.PackageManager.GET_META_DATA;
-import static android.content.pm.PackageManager.GET_SERVICES;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.PrintAttributes;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.uiautomator.UiDevice;
-
-import com.android.printspooler.outofprocess.tests.mockservice.PrintServiceCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.mockito.stubbing.Answer;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * This is the base class for print tests.
- */
-abstract class BasePrintTest {
-    protected static final long OPERATION_TIMEOUT = 30000;
-    private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
-    private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
-    private static String sDisabledPrintServicesBefore;
-
-    private android.print.PrintJob mPrintJob;
-
-    private static Instrumentation sInstrumentation;
-    private static UiDevice sUiDevice;
-
-    @Rule
-    public ActivityTestRule<PrintTestActivity> mActivityRule =
-            new ActivityTestRule<>(PrintTestActivity.class, false, true);
-
-    /**
-     * Return the UI device
-     *
-     * @return the UI device
-     */
-    public UiDevice getUiDevice() {
-        return sUiDevice;
-    }
-
-    protected static Instrumentation getInstrumentation() {
-        return sInstrumentation;
-    }
-
-    @BeforeClass
-    public static void setUpClass() throws Exception {
-        sInstrumentation = InstrumentationRegistry.getInstrumentation();
-        assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_PRINTING));
-
-        sUiDevice = UiDevice.getInstance(sInstrumentation);
-
-        // Make sure we start with a clean slate.
-        clearPrintSpoolerData();
-
-        disablePrintServices(sInstrumentation.getTargetContext().getPackageName());
-
-        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
-        // Dexmaker is used by mockito.
-        System.setProperty("dexmaker.dexcache", getInstrumentation()
-                .getTargetContext().getCacheDir().getPath());
-    }
-
-    @AfterClass
-    public static void tearDownClass() throws Exception {
-        enablePrintServices();
-    }
-
-    @Before
-    public void unlockScreen() throws Exception {
-        // Unlock screen.
-        runShellCommand("input keyevent KEYCODE_WAKEUP");
-        runShellCommand("wm dismiss-keyguard");
-    }
-
-    @After
-    public void exitActivities() throws Exception {
-        // Exit print spooler
-        getUiDevice().pressBack();
-        getUiDevice().pressBack();
-    }
-
-    protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
-            final PrintAttributes attributes) {
-        // Initiate printing as if coming from the app.
-        getInstrumentation().runOnMainSync(() -> {
-            PrintManager printManager = (PrintManager) getActivity()
-                    .getSystemService(Context.PRINT_SERVICE);
-            mPrintJob = printManager.print("Print job", adapter, attributes);
-        });
-
-        return mPrintJob;
-    }
-
-    protected PrintTestActivity getActivity() {
-        return mActivityRule.getActivity();
-    }
-
-    public static String runShellCommand(String cmd)
-            throws IOException {
-        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(cmd);
-        byte[] buf = new byte[512];
-        int bytesRead;
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        StringBuilder stdout = new StringBuilder();
-        while ((bytesRead = fis.read(buf)) != -1) {
-            stdout.append(new String(buf, 0, bytesRead));
-        }
-        fis.close();
-        return stdout.toString();
-    }
-
-    protected static void clearPrintSpoolerData() throws Exception {
-        assertTrue("failed to clear print spooler data", runShellCommand(
-                String.format("pm clear --user %d %s", CURRENT_USER_ID,
-                        PrintManager.PRINT_SPOOLER_PACKAGE_NAME)).contains(
-                PM_CLEAR_SUCCESS_OUTPUT));
-    }
-
-    /**
-     * Disable all print services beside the ones we want to leave enabled.
-     *
-     * @param packageToLeaveEnabled The package of the services to leave enabled.
-     */
-    private static void disablePrintServices(String packageToLeaveEnabled) throws IOException {
-        Instrumentation instrumentation = getInstrumentation();
-
-        sDisabledPrintServicesBefore = runShellCommand(
-                "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
-
-        Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
-        List<ResolveInfo> installedServices = instrumentation.getContext().getPackageManager()
-                .queryIntentServices(printServiceIntent, GET_SERVICES | GET_META_DATA);
-
-        StringBuilder builder = new StringBuilder();
-        for (ResolveInfo service : installedServices) {
-            if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
-                continue;
-            }
-            if (builder.length() > 0) {
-                builder.append(":");
-            }
-            builder.append(new ComponentName(service.serviceInfo.packageName,
-                    service.serviceInfo.name).flattenToString());
-        }
-
-        runShellCommand(
-                "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
-    }
-
-    /**
-     * Revert {@link #disablePrintServices(String)}
-     */
-    private static  void enablePrintServices() throws IOException {
-        runShellCommand("settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
-                        + sDisabledPrintServicesBefore);
-    }
-
-    @SuppressWarnings("unchecked")
-    protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
-            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
-            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
-            Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
-            Answer<Void> onDestroy) {
-        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
-
-        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
-        when(callbacks.getSession()).thenCallRealMethod();
-
-        if (onStartPrinterDiscovery != null) {
-            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
-                    any(List.class));
-        }
-        if (onStopPrinterDiscovery != null) {
-            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
-        }
-        if (onValidatePrinters != null) {
-            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
-                    any(List.class));
-        }
-        if (onStartPrinterStateTracking != null) {
-            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onRequestCustomPrinterIcon != null) {
-            doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
-                    any(PrinterId.class), any(CancellationSignal.class),
-                    any(CustomPrinterIconCallback.class));
-        }
-        if (onStopPrinterStateTracking != null) {
-            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
-                    any(PrinterId.class));
-        }
-        if (onDestroy != null) {
-            doAnswer(onDestroy).when(callbacks).onDestroy();
-        }
-
-        return callbacks;
-    }
-
-    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
-            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
-            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
-        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
-
-        doCallRealMethod().when(service).setService(any(PrintService.class));
-        when(service.getService()).thenCallRealMethod();
-
-        if (onCreatePrinterDiscoverySessionCallbacks != null) {
-            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
-                    .onCreatePrinterDiscoverySessionCallbacks();
-        }
-        if (onPrintJobQueued != null) {
-            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
-        }
-        if (onRequestCancelPrintJob != null) {
-            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
-                    any(PrintJob.class));
-        }
-
-        return service;
-    }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
deleted file mode 100644
index 4905a0b..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
+++ /dev/null
@@ -1,32 +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.printspooler.outofprocess.tests;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class PrintTestActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-    }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 78a0cac..7ebf93d 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -30,6 +30,11 @@
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.print.pdf.PrintedPdfDocument;
+import android.print.test.BasePrintTest;
+import android.print.test.services.AddPrintersActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.support.test.filters.LargeTest;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject;
@@ -38,11 +43,6 @@
 import android.support.test.uiautomator.Until;
 import android.util.Log;
 
-import com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity;
-import com.android.printspooler.outofprocess.tests.mockservice.MockPrintService;
-import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -62,10 +62,6 @@
 public class WorkflowTest extends BasePrintTest {
     private static final String LOG_TAG = WorkflowTest.class.getSimpleName();
 
-    private static float sWindowAnimationScaleBefore;
-    private static float sTransitionAnimationScaleBefore;
-    private static float sAnimatiorDurationScaleBefore;
-
     private PrintAttributes.MediaSize mFirst;
     private boolean mSelectPrinter;
     private PrintAttributes.MediaSize mSecond;
@@ -91,7 +87,7 @@
             throws TimeoutException, InterruptedException {
         long startTime = System.currentTimeMillis();
         while (condition.get()) {
-            long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime);
+            long timeLeft = OPERATION_TIMEOUT_MILLIS - (System.currentTimeMillis() - startTime);
             if (timeLeft < 0) {
                 throw new TimeoutException();
             }
@@ -156,7 +152,7 @@
      */
     private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef,
             ArrayList<String> trackedPrinters, PrintAttributes.MediaSize mediaSize) {
-        MockPrintService.setCallbacks(createMockPrintServiceCallbacks(
+        FirstPrintService.setCallbacks(createMockPrintServiceCallbacks(
                 inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> {
                             synchronized (sessionRef) {
                                 sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock())
@@ -243,7 +239,7 @@
                     callback.onWriteFailed(e.getMessage());
                 }
             }
-        }, null);
+        }, (PrintAttributes) null);
     }
 
     @Parameterized.Parameters
@@ -303,7 +299,7 @@
         } else {
             Log.i(LOG_TAG, "Waiting for error message");
             assertNotNull(getUiDevice().wait(Until.findObject(
-                    By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+                    By.text("This printer isn't available right now.")), OPERATION_TIMEOUT_MILLIS));
         }
 
         setPrinter("All printers\u2026");
@@ -316,7 +312,7 @@
                 () -> addPrinter(session[0], "2nd printer", mSecond));
 
         // This executes the observer registered above
-        clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
+        clickOn(new UiSelector().text(FirstPrintService.class.getCanonicalName())
                 .resourceId("com.android.printspooler:id/title"));
 
         getUiDevice().pressBack();
@@ -342,7 +338,8 @@
             } else {
                 Log.i(LOG_TAG, "Waiting for error message");
                 assertNotNull(getUiDevice().wait(Until.findObject(
-                        By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+                        By.text("This printer isn't available right now.")),
+                        OPERATION_TIMEOUT_MILLIS));
             }
 
             Log.i(LOG_TAG, "Waiting for 1st printer to be not tracked");
@@ -370,7 +367,8 @@
             } else {
                 Log.i(LOG_TAG, "Waiting for error message");
                 assertNotNull(getUiDevice().wait(Until.findObject(
-                        By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+                        By.text("This printer isn't available right now.")),
+                        OPERATION_TIMEOUT_MILLIS));
             }
 
             Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
deleted file mode 100644
index 2ea4e7d..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
+++ /dev/null
@@ -1,52 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-
-public class AddPrintersActivity extends Activity {
-    private static final ArrayList<Runnable> sObservers = new ArrayList<>();
-
-    public static void addObserver(@NonNull Runnable observer) {
-        synchronized (sObservers) {
-            sObservers.add(observer);
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        synchronized (sObservers) {
-            for (Runnable sObserver : sObservers) {
-                sObserver.run();
-            }
-        }
-
-        finish();
-    }
-
-    public static void clearObservers() {
-        synchronized (sObservers) {
-            sObservers.clear();
-        }
-    }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java
deleted file mode 100644
index 3a23113..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.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 com.android.printspooler.outofprocess.tests.mockservice;
-
-public class MockPrintService extends StubbablePrintService {
-
-    private static final Object sLock = new Object();
-
-    private static PrintServiceCallbacks sCallbacks;
-
-    public static void setCallbacks(PrintServiceCallbacks callbacks) {
-        synchronized (sLock) {
-            sCallbacks = callbacks;
-        }
-    }
-
-    @Override
-    protected PrintServiceCallbacks getCallbacks() {
-        synchronized (sLock) {
-            if (sCallbacks != null) {
-                sCallbacks.setService(this);
-            }
-            return sCallbacks;
-        }
-    }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
deleted file mode 100644
index 07baa0f..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
+++ /dev/null
@@ -1,39 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-
-public abstract class PrintServiceCallbacks {
-
-    private PrintService mService;
-
-    public PrintService getService() {
-        return mService;
-    }
-
-    public void setService(PrintService service) {
-        mService = service;
-    }
-
-    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
-
-    public abstract void onRequestCancelPrintJob(PrintJob printJob);
-
-    public abstract void onPrintJobQueued(PrintJob printJob);
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java
deleted file mode 100644
index 5c1260c..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.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 com.android.printspooler.outofprocess.tests.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-
-import java.util.List;
-
-public abstract class PrinterDiscoverySessionCallbacks {
-
-    private StubbablePrinterDiscoverySession mSession;
-
-    public void setSession(StubbablePrinterDiscoverySession session) {
-        mSession = session;
-    }
-
-    public StubbablePrinterDiscoverySession getSession() {
-        return mSession;
-    }
-
-    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
-
-    public abstract void onStopPrinterDiscovery();
-
-    public abstract void onValidatePrinters(List<PrinterId> printerIds);
-
-    public abstract void onStartPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
-            CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
-
-    public abstract void onStopPrinterStateTracking(PrinterId printerId);
-
-    public abstract void onDestroy();
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
deleted file mode 100644
index be9d19b..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
+++ /dev/null
@@ -1,52 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-public abstract class StubbablePrintService extends PrintService {
-
-    @Override
-    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            return new StubbablePrinterDiscoverySession(this,
-                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
-        }
-        return null;
-    }
-
-    @Override
-    public void onRequestCancelPrintJob(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onRequestCancelPrintJob(printJob);
-        }
-    }
-
-    @Override
-    public void onPrintJobQueued(PrintJob printJob) {
-        PrintServiceCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.onPrintJobQueued(printJob);
-        }
-    }
-
-    protected abstract PrintServiceCallbacks getCallbacks();
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
deleted file mode 100644
index a828cd6..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
+++ /dev/null
@@ -1,95 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-
-public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
-    private final PrintService mService;
-    private final PrinterDiscoverySessionCallbacks mCallbacks;
-
-    public StubbablePrinterDiscoverySession(PrintService service,
-            PrinterDiscoverySessionCallbacks callbacks) {
-        mService = service;
-        mCallbacks = callbacks;
-        if (mCallbacks != null) {
-            mCallbacks.setSession(this);
-        }
-    }
-
-    public PrintService getService() {
-        return mService;
-    }
-
-    @Override
-    public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterDiscovery(priorityList);
-        }
-    }
-
-    @Override
-    public void onStopPrinterDiscovery() {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterDiscovery();
-        }
-    }
-
-    @Override
-    public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
-        if (mCallbacks != null) {
-            mCallbacks.onValidatePrinters(printerIds);
-        }
-    }
-
-    @Override
-    public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStartPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull CustomPrinterIconCallback callback) {
-        if (mCallbacks != null) {
-            mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
-        }
-    }
-
-    @Override
-    public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
-        if (mCallbacks != null) {
-            mCallbacks.onStopPrinterStateTracking(printerId);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mCallbacks != null) {
-            mCallbacks.onDestroy();
-        }
-    }
-}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index cf9515e..a7a7ea9 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Kon nie instellings vir <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> oopmaak nie"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Die invoermetode kan dalk alle teks wat jy invoer, versamel, insluitend persoonlike data soos wagwoorde en kredietkaartnommers. Dit kom van die program <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Wil jy dié invoermetode gebruik?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Let wel: Ná \'n herselflaai kan hierdie program nie begin voordat jy jou foon ontsluit het nie"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registrasiestaat"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Geregistreer"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nie geregistreer nie"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Onbeskikbaar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index a84ee81..bff8fe2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"የ<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ቅንብሮች መክፈት አልተሳካም"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ይህ ግቤት ስልት የሚትተይበውን ፅሁፍ ሁሉ፣  እንደይለፍ ቃል እና የብድር ካርድ ጨምሮ የግል ውሂብ ምናልባት መሰብሰብ ይችላል። ከትግበራው ይመጣል። <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ይህን ግቤት ስልትይጠቀም?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ማስታወሻ፦ እንደገና ከማስነሳት በኋላ ይህ መተግበሪያ ስልክዎን እስከሚከፍቱት ድረስ ሊጀምር አይችልም"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"የIMS ምዝገባ ቀን"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"የተመዘገበ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"አልተመዘገበም"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"አይገኝም"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index b9a16b96..457a6dd 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"تعذّر فتح الإعدادات لـ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"يمكن أن يكون أسلوب الإدخال هذا قادرًا على جمع كل النصوص التي تكتبها، بما في ذلك البيانات الشخصية مثل كلمات المرور وأرقام بطاقات الائتمان. يتم الحصول على هذا الأسلوب من التطبيق <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. هل تريد استخدام أسلوب الإدخال هذا؟"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ملاحظة: بعد إعادة التشغيل، يتعذر بدء هذا التطبيق إلى أن تلغي قفل هاتفك."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"‏حالة تسجيل IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"مُسجَّل"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"غير مُسجَّل"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"غير متاح"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 0a86706..8b453ab 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> üçün ayarları açmaq alınmadı"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Bu daxiletmə metodu yazdığınız bütün mətni toplaya bilər. Buna kredit kart kimi şəxsi məlumat aid ola bilər. Kökü <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> tətbiqindədir. Bu daxiletmə metodu istifadə olunsun?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Qeyd: Yenidən yüklənmədən sonra, bu cihazın kilidini açmamış tətbiq başlaya bilməz"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS qeydiyyat statusu"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Qeydiyyatlı"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Qeydiyyatsız"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Əlçatmazdır"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 1e84d3d..ba0de57 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje podešavanja za aplikaciju <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspelo"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj metod unosa možda može da prikuplja sav tekst koji unosite, uključujući lične podatke, kao što su lozinke i brojevi kreditnih kartica. Potiče od aplikacije <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Želite li da koristite ovaj metod unosa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Posle restartovanja ova aplikacija ne može da se pokrene dok ne otključate telefon"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Status IMS registracije"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrovan je"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrovan"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nedostupno"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e47bc43..49a18b6 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не атрымалася адкрыць параметры <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Гэты метад уводу можа збіраць увесь тэкст, які ўводзіцца, у тым лiку такiя персанальныя дадзеныя, як паролі і нумары крэдытных карт. Ён выкарыстоўваецца прыкладаннем <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Выкарыстоўваць гэты метад уводу?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Заўвага. Пасля перазагрузкі гэта праграма не зможа запусціцца, пакуль вы не разблакіруеце тэлефон"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Стан рэгістрацыі IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Зарэгістраваны"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зарэгістраваны"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Адсутнічае"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 77beb4c..8fbef19 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Отварянето на настройките за <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> не бе успешно"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Този метод за въвеждане може да събира целия въведен от вас текст, включително лични данни като пароли и номера на кредитни карти. Той произлиза от приложението <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Искате ли да го използвате?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Забележка: След рестартиране това приложение не може да се стартира, докато не отключите телефона си"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Състояние на регистрацията за незабавни съобщения"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистрирано"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не е регистрирано"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Няма данни"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 424e7b9..ec48835 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> জন্য সেটিংস খুলতে ব্যর্থ হয়েছে"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"এই ইনপুট পদ্ধতিটি হয়তো পাসওয়ার্ড এবং ক্রেডিট কার্ড নম্বর সহ আপনার টাইপ করা সমস্ত টেক্সট সংগ্রহ করতে সক্ষম হতে পারে। এটি <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> অ্যাপ থেকে এসেছে। এই ইনপুট পদ্ধতিটি ব্যবহার করবেন?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"দ্রষ্টব্য: পুনরায় চালু করার পরে, আপনি আপনার ফোন আনলক না করা পর্যন্ত এই অ্যাপটিকে চালু করতে পারবেন না"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS রেজিস্ট্রেশনের স্থিতি"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"রেজিস্টার করা"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"রেজিস্টার করা নয়"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"অনুপলব্ধ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 97cbfbf..a331286 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -110,7 +110,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
-    <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB dijeljenje veze"</string>
+    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Dijeljenje veze"</string>
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nije uspjelo otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji otkucate, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Nakon ponovnog pokretanja, ova aplikacija se neće moći pokrenuti dok ne otključate telefon"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stanje IMS registracije"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrirano"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nije dostupno"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 479cf38..145f61c 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"No s\'ha pogut obrir la configuració de: <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Pot ser que aquest mètode d\'introducció pugui recopilar tot el que escriviu, incloses dades personals, com ara contrasenyes i números de targetes de crèdit. Ve de l\'aplicació <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voleu utilitzar aquest mètode d\'introducció?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: després de reiniciar, l\'aplicació no s\'iniciarà fins que no desbloquegis el telèfon"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estat del registre d\'IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrat"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Sense registrar"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 97a76f7..994afae 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nastavení aplikace <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> se nepodařilo otevřít"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Prostřednictvím této vstupní metody zadávání dat lze shromažďovat zadaný text včetně osobních údajů, jako jsou hesla a čísla platebních karet. Metoda je poskytována aplikací <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Chcete tuto metodu zadávání dat použít?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Poznámka: Po restartování se tato aplikace nespustí, dokud telefon neodemknete."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stav registrace IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrováno"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neregistrováno"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Není k dispozici"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6a006cc..e916853 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -329,7 +329,7 @@
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede filkrypteret"</string>
     <string name="title_convert_fbe" msgid="1263622876196444453">"Konverterer til filbaseret kryptering"</string>
-    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Konvertér datapartitionen til filbaseret kryptering.\nAdvarsel! Alle dine data vil blive slettet.\n Dette er en alfafunktion, og den fungerer muligvis ikke korrekt.\n Tryk på \"Ryd og konvertér…\" for at fortsætte."</string>
+    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Konvertér datapartitionen til filbaseret kryptering.\nAdvarsel! Alle dine data vil blive fuldstændigt slettet.\n Dette er en alfafunktion, og den fungerer muligvis ikke korrekt.\n Tryk på \"Ryd og konvertér…\" for at fortsætte."</string>
     <string name="button_convert_fbe" msgid="5152671181309826405">"Ryd og konvertér…"</string>
     <string name="picture_color_mode" msgid="4560755008730283695">"Farvetilstand for billeder"</string>
     <string name="picture_color_mode_desc" msgid="1141891467675548590">"Brug sRGB"</string>
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Indstillingerne for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> kunne ikke åbnes"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Denne inputmetode kan muligvis indsamle al indtastet tekst, f.eks. personlige data såsom adgangskoder og kreditkortnumre. Den kommer fra appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vil du anvende denne inputmetode?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Bemærk! Efter en genstart kan denne app ikke starte, før du låser din telefon op"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Status for IMS-registrering"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registreret"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ikke registreret"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Utilgængelig"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2203bc6..1ec4fee 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Einstellungen für <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> konnten nicht geöffnet werden."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Diese Eingabemethode kann den gesamten von dir eingegebenen Text erfassen, einschließlich personenbezogener Daten wie Passwörter und Kreditkartennummern. Sie ist Teil der App \"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>\". Möchtest du diese Eingabemethode verwenden?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Hinweis: Nach einem Neustart wird diese App erst gestartet, wenn du dein Smartphone entsperrst"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-Registrierungsstatus"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registriert"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nicht registriert"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nicht verfügbar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 514cfd8..2548a5d 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Δεν ήταν δυνατό το άνοιγμα των ρυθμίσεων για την εφαρμογή <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Αυτή η μέθοδος εισαγωγής ενδέχεται να έχει τη δυνατότητα να συλλέξει όλα τα κείμενα που πληκτρολογείτε, συμπεριλαμβανομένων προσωπικών δεδομένων, όπως είναι οι κωδικοί πρόσβασης και οι αριθμοί πιστωτικής κάρτας. Προέρχεται από την εφαρμογή <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Να γίνει χρήση αυτής της μεθόδου εισαγωγής;"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Σημείωση: Μετά από μια επανεκκίνηση, δεν είναι δυνατή η έναρξη αυτής της συσκευής προτού ξεκλειδώσετε το τηλέφωνό σας"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Κατάσταση εγγραφής υπηρεσίας IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Εγγεγραμμένη"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Μη εγγεγραμμένη"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Μη διαθέσιμο"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 66d8fe0..77c0e4f 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎Failed to open settings for ‎‏‎‎‏‏‎<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎‎This input method may be able to collect all the text you type, including personal data like passwords and credit card numbers. It comes from the app ‎‏‎‎‏‏‎<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Use this input method?‎‏‎‎‏‎"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎Note: After a reboot, this app can\'t start until you unlock your phone‎‏‎‎‏‎"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‏‎IMS registration state‎‏‎‎‏‎"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‎Registered‎‏‎‎‏‎"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎Not registered‎‏‎‎‏‎"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎Unavailable‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 743700e..ff26eef 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Se produjo un error al abrir la configuración de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"El método de entrada puede recopilar todo el texto que escribas, incluidos los datos personales como contraseñas y números de tarjetas de crédito. Proviene de la aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ¿Deseas utilizar este método de entrada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Luego de reiniciar el dispositivo, esta app no podrá iniciarse hasta que desbloquees tu teléfono"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado de registro de IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Sin registrar"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d964728..20bab13 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Error al abrir los ajustes de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada puede registrar todo lo que escribas, incluidos datos personales, como las contraseñas y los números de las tarjetas de crédito. Procede de la aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ¿Quieres usar este método de entrada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Después de reiniciar, tienes que desbloquear el teléfono para que esta aplicación se pueda iniciar"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado del registro de IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"No registrado"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index cfcc4c6..a0341fb 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Rakenduse <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> seadete avamine ebaõnnestus"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"See sisestusmeetod võib koguda kogu teie sisestatava teksti, sh isikuandmed (nt paroolid ja krediitkaardinumbrid). See pärineb rakendusest <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Kas soovite seda sisestusmeetodit kasutada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Märkus. Pärast taaskäivitamist ei saa see rakendus käivituda enne, kui olete telefoni avanud"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-i registreerimise olek"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registreeritud"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ei ole registreeritud"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Pole saadaval"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index d7595e8..e8d9a5e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ezin izan dira <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> aplikazioaren ezarpenak ireki."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Idazketa-metodoak idazten duzun testu guztia bil dezake, pasahitzak eta kreditu-txarteleko zenbakiak bezalako datu pertsonalak barne. <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> aplikazioak egin du eskaera. Idazketa-metodo hori erabili nahi duzu?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Oharra: berrabiarazi ondoren, ezin izango da abiarazi aplikazio hau telefonoa desblokeatzen duzun arte"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS erregistratzearen egoera"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Erregistratuta"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Erregistratu gabe"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ez dago erabilgarri"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f582486..326192d 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"تنظیمات <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> بازنشد"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"این روش ورودی ممکن است بتواند تمام متنی را که تایپ می‌کنید جمع‌آوری کند، از جمله اطلاعات شخصی مانند گذرواژه‌ها و شماره‌های کارت اعتباری. این روش توسط برنامه <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ارائه می‌شود. از این روش ورودی استفاده می‌کنید؟"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"توجه: بعد از راه‌اندازی تا زمانی‌که قفل تلفنتان را باز نکنید، این برنامه نمی‌تواند شروع شود"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"‏وضعیت ثبت IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ثبت‌شده"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ثبت نشده است"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"در دسترس نیست"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 83073d1..6321086 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Sovelluksen <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> asetuksia ei voi avata"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Tämä syöttötapa saattaa kerätä kaiken kirjoittamasi tekstin, mukaan luettuna henkilökohtaiset tiedot kuten salasanat ja luottokortin numerot. Se on lähtöisin sovelluksesta <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Käytetäänkö tätä syöttötapaa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Huom. Jotta voit käynnistää tämän sovelluksen uudelleenkäynnistyksen jälkeen, sinun täytyy avata puhelimen lukitus."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-rekisteröinnin tila"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Rekisteröity"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ei rekisteröity"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ei käytettävissä"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 54d6864..00c02ce 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Échec de l\'ouverture des paramètres de l\'application <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ce mode de saisie est susceptible d\'enregistrer le texte que vous saisissez, y compris vos données personnelles, telles que les mots de passe et les numéros de carte de paiement. Il provient de l\'application <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voulez-vous vraiment l\'activer?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Remarque : Après un redémarrage, vous ne pouvez pas lancer cette application tant que vous n\'avez pas déverrouillé votre téléphone."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"État d\'enregistrement IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Enregistré"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non enregistré"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Non accessible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index e35d330..a5625e4 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Échec de l\'ouverture des paramètres de l\'application <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ce mode de saisie est susceptible d\'enregistrer le texte que vous saisissez, y compris vos données personnelles, telles que les mots de passe et les numéros de carte de paiement. Il provient de l\'application <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voulez-vous vraiment l\'activer ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Remarque : Après un redémarrage, vous ne pouvez pas lancer cette application tant que vous n\'avez pas déverrouillé votre téléphone."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"État de l\'enregistrement IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Enregistré"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non enregistré"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Non disponible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8291cb1..e1ea694 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Non se puido abrir a configuración de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"É posible que este método de entrada poida recompilar todo o texto que escribas, incluídos os datos persoais como os contrasinais e os números de tarxetas de crédito. Provén da aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Queres usar este método de entrada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Tras un reinicio, non se pode iniciar esta aplicación ata que desbloquees o teléfono"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado de rexistro de IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Rexistrado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non rexistrado"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Non dispoñible"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index ca14118..0e8dd47 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> માટેની સેટિંગ્સ ખોલવામાં નિષ્ફળ"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"આ ઇનપુટ પદ્ધતિ પાસવર્ડ્સ અને ક્રેડિટ કાર્ડ નંબર જેવી વ્યક્તિગત માહિતી સહિત તમે લખો છો તે તમામ ટેક્સ્ટ એકત્રિત કરવા માટે સક્ષમ હોઈ શકે છે. તે ઍપ્લિકેશન <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> માંથી આવે છે. આ ઇનપુટ પદ્ધતિ વાપરીએ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"નોંધ: રીબૂટ કર્યાં પછી, જ્યાં સુધી તમે તમારો ફોન અનલૉક કરશો નહીં ત્યાં સુધી આ ઍપ્લિકેશન શરૂ થઈ શકશે નહીં"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS રજિસ્ટ્રેશનની સ્થિતિ"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"રજિસ્ટર કરેલ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"રજિસ્ટર કરેલ નથી"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"અનુપલબ્ધ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bde80f2..28bc68a 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> के लिए सेटिंग खोलने में विफल रहा"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"यह इनपुट विधि, पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा सहित आपके द्वारा लिखे जाने वाले सभी लेख को एकत्र कर सकती है. यह <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ऐप्स से आती है. इस इनपुट विधि का उपयोग करें?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"नोट: पुनः बूट करने के बाद, यह ऐप्लिकेशन तब तक शुरू नहीं हो सकता है जब तक कि आप अपना फ़ोन अनलॉक ना कर लें"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS रजिस्ट्रेशन की स्थिति"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"रजिस्टर है"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"रजिस्टर नहीं है"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ec2b9cf..b5c5439 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje postavki za aplikaciju <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspjelo"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa možda može prikupljati sav tekst koji unosite, uključujući osobne podatke poput zaporki i brojeva kreditnih kartica. To omogućuje aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Upotrijebiti taj način unosa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: ova se aplikacija ne može pokrenuti nakon ponovnog pokretanja dok ne otključate telefon"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stanje registracije IMS-a"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrirano"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nije dostupno"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 3fe6a78..47d6c98 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nem sikerült megnyitni a(z) <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> beállításait."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ez a beviteli módszer alkalmas lehet a beírt szövegek – köztük az olyan személyes adatok, mint a jelszavak és a hitelkártyaszámok - összegyűjtésére. A <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> alkalmazás kapcsolta be. Használja ezt a módszert?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Megjegyzés: Újraindítás után ez az alkalmazás csak a telefon feloldását követően indul el"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-regisztráció állapota"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Regisztrált"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nem regisztrált"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nem érhető el"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 119e24c..6f11cd8 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Չստացվեց ցույց տալ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-ի տվյալները"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Այս ներմուծման եղանակը հնարավորություն է տալիս հավաքել ձեր մուտքագրած ողջ տեքստը՝ ընդգրկելով անձնական տեղեկություններ, ինչպես գաղտնաբառն ու բանկային քարտի համարները: Սկզբնաղբյուրը <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> հավելվածն է: Կիրառե՞լ այս եղանակը:"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Ուշադրություն. Վերաբեռնումից հետո այս հավելվածը չի գործարկվի մինչև չապակողպեք հեռախոսը"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS ծառայության գրանցման կարգավիճակը"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Գրանցված է"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Գրանցված չէ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Անհասանելի"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c2db508..b4a58bb 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Gagal membuka setelan untuk <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Metode masukan ini mungkin dapat mengumpulkan semua teks yang Anda ketik, termasuk data pribadi seperti sandi dan nomor kartu kredit. Metode ini berasal dari aplikasi <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gunakan metode masukan ini?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Catatan: Setelah boot ulang, aplikasi ini tidak dapat dimulai hingga kunci ponsel dibuka"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Status pendaftaran IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Terdaftar"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Tidak terdaftar"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Tidak Tersedia"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 883e909..29b1b5d 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ekki tókst að opna stillingar <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Þessi innsláttaraðferð getur hugsanlega skráð allan texta sem þú slærð inn, þ. á m. persónuupplýsingar á borð við aðgangsorð og kreditkortanúmer. Hún kemur frá forritinu <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Viltu nota þessa innsláttaraðferð?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Athugaðu: Eftir endurræsingu er ekki hægt að ræsa þetta forrit fyrr en þú tekur símann úr lás"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Staða IMS-skráningar"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Skráð"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ekki skráð"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ekki tiltækt"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index e53e4da..46004c3 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Impossibile aprire le impostazioni di <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Questo metodo di immissione potrebbe riuscire a raccogliere tutto il testo digitato, compresi i dati personali come password e numeri di carte di credito. Deriva dall\'applicazione <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizzare questo metodo di immissione?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: dopo il riavvio, devi sbloccare il telefono per poter avviare l\'app."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stato di registrazione IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrato"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non registrato"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Non disponibile"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 363b064..24f4684 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"פתיחת הגדרות עבור <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> נכשלה"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ייתכן ששיטת קלט זו תוכל לאסוף את כל הטקסט שאתה מקליד, כולל נתונים אישיים כגון סיסמאות ומספרי כרטיס אשראי. היא מגיעה מהאפליקציה <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. האם להשתמש בשיטת קלט זו?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"שים לב: לאחר הפעלה מחדש של המכשיר, ניתן להפעיל את האפליקציה רק לאחר שתבטל את נעילת הטלפון"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"‏סטטוס הרשמה ל-IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"רשום"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"לא רשום"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"לא זמין"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index dfd199a..bb3f5a9 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -390,4 +390,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>の設定を開くことができませんでした"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"この入力方法を選択すると、すべての入力内容の収集をアプリ(<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>)に許可することになります。これにはパスワードやクレジットカード番号などの個人情報も含まれます。この入力方法を使用しますか?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注: 再起動後、スマートフォンのロックを解除するまでこのアプリを起動することはできません"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS 登録ステータス"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"登録済み"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未登録"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"不明"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 7fac639..2280261 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-ისთვის პარამეტრების გახსნა ვერ მოხერხდა"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"შეყვანის ამ მეთოდმა შესაძლოა მოახერხოს თქვენი აკრეფილი ყველა ტექსტის, მათ შორის პერსონალური მონაცემების, პაროლებისა და საკრედიტო ბარათის ნომრების შენახვა. ეს მეთოდი ეკუთვნის <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>-ს. გამოვიყენო შეყვანის ეს მეთოდი?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"შენიშვნა: გადატვირთვის შემდეგ, ეს აპი ვერ გაეშვება, სანამ ტელეფონს არ განბლოკავთ"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS რეგისტრაციის სტატუსი"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"რეგისტრირებული"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"არარეგისტრირებული"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"მიუწვდომელია"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3b806a5..03bd3cd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> қолданбасы үшін параметрлерді ашу орындалмады"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Бұл енгізу әдісі сіз терген барлық мәтінді, кілтсөз және кредит карта нөмірлері сияқты жеке ақпаратты қоса, жинауы мүмкін. Бұл <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> қолданбасы арқылы жасалады. Осы әдіс қолданылсын ба?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Ескертпе: қайта жүктегеннен кейін, телефонның құлпын ашпайынша, бұл қолданба іске қосылмайды"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS тіркеу күйі"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Тіркелген"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Тіркелмеген"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Қол жетімсіз"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2160bae..6414141 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"បាន​បរាជ័យ​ក្នុង​ការ​បើក​ការ​កំណត់​សម្រាប់ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"វិធី​សាស្ត្រ​បញ្ចូល​នេះ​អាច​​ប្រមូល​អត្ថបទ​ដែល​អ្នក​វាយ​ទាំងអស់ រួម​មាន​ទិន្នន័យ​ផ្ទាល់ខ្លួន ដូច​ជ ពាក្យ​សម្ងាត់ និង​លេខ​កាត​ឥណទាន។ វា​បាន​មក​ពី​កម្មវិធី <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ។ ប្រើ​វិធី​សាស្ត្រ​បញ្ចូល​នេះ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ចំណាំ៖ បន្ទាប់ពីបិទបើកឡើងវិញហើយ កម្មវិធីនេះមិនអាចចាប់ផ្តើមបានទេ រហូតទាល់តែអ្នកដោះសោទូរស័ព្ទរបស់អ្នក"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"ស្ថានភាព​នៃការ​ចុះឈ្មោះ IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"បាន​ចុះឈ្មោះ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"មិនបាន​ចុះឈ្មោះ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"មិន​មាន"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a47a92c..d108dd6 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -121,7 +121,7 @@
     <string name="running_process_item_user_label" msgid="3129887865552025943">"ಬಳಕೆದಾರ: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="313159469856372621">"ಕೆಲವು ಡೀಫಾಲ್ಟ್‌ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿದೆ"</string>
     <string name="launch_defaults_none" msgid="4241129108140034876">"ಡೀಫಾಲ್ಟ್‌ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿಲ್ಲ"</string>
-    <string name="tts_settings" msgid="8186971894801348327">"ಪಠ್ಯದಿಂದ ಧ್ವನಿಗೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="tts_settings" msgid="8186971894801348327">"ಪಠ್ಯದಿಂದ ಧ್ವನಿಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="tts_settings_title" msgid="1237820681016639683">"ಧ್ವನಿಗೆ-ಪಠ್ಯದ ಔಟ್‌ಪುಟ್‌"</string>
     <string name="tts_default_rate_title" msgid="6030550998379310088">"ಧ್ವನಿಯ ಪ್ರಮಾಣ"</string>
     <string name="tts_default_rate_summary" msgid="4061815292287182801">"ಪಠ್ಯವನ್ನು ಹೇಳಿದ ವೇಗ"</string>
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಲು ವಿಫಲವಾಗಿದೆ"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ವೈಯಕ್ತಿಕ ಡೇಟಾಗಳಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನೀವು ಟೈಪ್ ಮಾಡುವ ಎಲ್ಲ ಪಠ್ಯವನ್ನು ಸಂಗ್ರಹಿಸಲು ಈ ಇನ್‌ಪುಟ್ ವಿಧಾನಕ್ಕೆ ಸಾಧ್ಯವಾಗಬಹುದು. ಇದು <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಬರುತ್ತದೆ. ಈ ಇನ್‌ಪುಟ್ ವಿಧಾನವನ್ನು ಬಳಸುವುದೇ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ಗಮನಿಸಿ: ರೀಬೂಟ್ ನಂತರ, ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಅನ್‌ಲಾಕ್ ಮಾಡುವ ತನಕ ಈ ಆಪ್ ಪ್ರಾರಂಭಗೊಳ್ಳುವುದಿಲ್ಲ"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS ನೋಂದಣಿ ಸ್ಥಿತಿ"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ನೋಂದಾಯಿಸಲಾಗಿದೆ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ನೋಂದಾಯಿಸಲಾಗಿಲ್ಲ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ಲಭ್ಯವಿಲ್ಲ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 852c78e..ec03fb1 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 설정을 열지 못했음"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> 앱에서 지원하는 이 입력 방법을 사용하면 비밀번호 및 신용카드 번호와 같은 개인 정보를 비롯하여 입력한 모든 텍스트가 수집될 수 있습니다. 사용하시겠습니까?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"참고: 재부팅한 후 이 앱은 휴대전화를 잠금 해제해야 시작됩니다."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS 등록 상태"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"등록됨"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"등록되지 않음"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"사용할 수 없음"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 380d0ff..883811d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> тууралоолору ачылган жок"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Бул киргизүү ыкмасы сиз терген бардык тексттер, сырсөздөр жана кредиттик  карталар сыяктуу жеке маалыматтарды кошо чогултушу мүмкүн. Бул <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> колдонмосу менен байланыштуу. Ушул киргизүү ыкма колдонулсунбу?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Эскертүү: Өчүрүп-күйгүзгөндөн кийин, бул колдонмо телефондун кулпусу ачылмайынча иштебейт"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS каттоо абалы"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Катталган"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Катталган эмес"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Жеткиликсиз"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index eb10dbb..464b490 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"ລົ້ມເຫລວໃນການເປີດການຕັ້ງຄ່າຂອງ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ວິທີການປ້ອນຂໍ້ມູນນີ້ ອາດສາມາດເກັບກຳທຸກຂໍ້ຄວາມທີ່ທ່ານພິມ, ຮວມເຖິງຂໍ້ມູນສ່ວນໂຕເຊັ່ນລະຫັດຜ່ານ ແລະໝາຍເລກບັດເຄຣດິດນຳ. ມັນມາຈາກແອັບຯ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ທ່ານຕ້ອງການໃຊ້ວິທີການປ້ອນຂໍ້ມູນນີ້ບໍ່?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ໝາຍເຫດ: ຫຼັງຈາກເປີດຂຶ້ນມາໃໝ່ແລ້ວ, ແອັບນີ້ຈະບໍ່ສາມາດເລີ່ມໄດ້ຈົນກວ່າທ່ານຈະປົດລັອກໂທລະສັບຂອງທ່ານ"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"ສະຖານະການລົງທະບຽນ IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ລົງທະບຽນແລ້ວ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ບໍ່ໄດ້ລົງທະບຽນ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ບໍ່ມີຂໍ້ມູນ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index adf6f8a..fd3bc18 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nepavyko atidaryti „<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>“ nustatymų"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Naudojant šį įvesties metodą galima rinkti visą įvedamą tekstą, įskaitant asmeninius duomenis, pvz., slaptažodžius ir kredito kortelių numerius. Jis pateikiamas naudojant programą „<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>“. Naudoti šį įvesties metodą?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Pastaba: paleidus iš naujo nebus galima paleisti programos, kol neatrakinsite telefono"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registracijos būsena"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Užregistruota"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neužregistruota"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Užimta"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 280900a..a6d308a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Neizdevās atvērt lietotnes <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> iestatījumus."</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Izmantojot šo ievades metodi, var tikt vākta informācija par visu ierakstīto tekstu, tostarp personiskiem datiem, piemēram, parolēm un kredītkaršu numuriem. Šī metode ir saistīta ar lietotni <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vai lietot šo ievades metodi?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Piezīme. Šo lietotni pēc atkārtotas palaišanas nevarēs startēt, kamēr netiks atbloķēts tālrunis."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS reģistrācijas statuss"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Reģistrēts"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nav reģistrēts"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nepieejams"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 51697bd..8882cb7 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Подесувањата за <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> не се отворија"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Овој метод на внес може да го собере сиот текст кој го пишувате, вклучувајќи и лични податоци како што се, лозинки и броеви на кредитни картички. Тоа го прави апликацијата <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Користи го овој метод на внес?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Забелешка: по рестартирање, апликацијава не може да се вклучи додека не го отклучите телефонот"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Состојба на IMS-регистрација"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистриран"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не е регистриран"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Недостапен"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index d96607e..8f711b3 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> എന്നതിനായുള്ള ക്രമീകരണങ്ങൾ തുറക്കുന്നതിൽ പരാജയപ്പെട്ടു"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"പാസ്‌വേഡുകൾ, ക്രെഡിറ്റ് കാർഡ് നമ്പറുകൾ എന്നിവ പോലുള്ള വ്യക്തിഗതമായ ഡാറ്റയുൾപ്പെടെ നിങ്ങൾ ടൈപ്പുചെയ്യുന്ന എല്ലാ വാചകവും ഈ ടൈപ്പുചെയ്യൽ രീതിയ്‌ക്ക് ശേഖരിക്കാനായേക്കും. ഇത് <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> അപ്ലിക്കേഷനിൽ നിന്നും വരുന്നു. ഈ ടൈപ്പുചെയ്യൽ രീതി ഉപയോഗിക്കണോ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ശ്രദ്ധിക്കുക: റീബൂട്ടിന് ശേഷം, ഫോൺ അൺലോക്കുചെയ്യുന്നത് വരെ ഈ ആപ്പ് ആരംഭിക്കാൻ കഴിയില്ല"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS രജിസ്‌ട്രേഷൻ നില"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"രജിസ്റ്റർ ചെയ്‌തു"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"രജിസ്‌റ്റർ ചെയ്‌തിട്ടില്ല"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ലഭ്യമല്ല"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index e13886d..2e1f5e6 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-н тохиргоог нээж чадсангүй"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Энэ оруулах арга нь таны нууц үгс, зээлийн картын дугаар гэх мэт бичсэн хувийн мэдээллийг цуглуулах боломжтой байж болно. Үүнийг <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> апп нийлүүлдэг. Энэ оруулах аргыг ашиглах уу?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Санамж: Дахин асаасны дараа энэ апп нь таныг утасны түгжээгээ тайлах хүртэл эхлэх боломжгүй"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS бүртгэлийн байдал"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Бүртгэсэн"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Бүртгээгүй"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Байхгүй"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index ae61916..63e2e87 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> साठी सेटिंग्ज उघडण्यात अयशस्वी"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ही इनपुट पद्धत संकेतशब्द आणि क्रेडिट कार्ड नंबर यासह, आपण टाइप करता तो सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. ही <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अॅपवरून येते. ही इनपुट पद्धत वापरायची?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही आपला फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS नोंदणी स्थिती"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"नोंदवलेले"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"नोंदवलेले नाही"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a44e5b1..9d81cea 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Gagal membuka tetapan untuk <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Kaedah input ini mungkin boleh mengumpulkan semua teks yang anda taipkan, termasuk data peribadi seperti kata laluan dan nombor kad kredit. Ia datang daripada aplikasi <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gunakan kaedah input ini?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Perhatian: Selepas but semula, apl ini tidak dapat dimulakan sehingga anda membuka kunci telefon"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Keadaan pendaftaran IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Berdaftar"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Tidak didaftarkan"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Tidak tersedia"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index af95eac..aa241a9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>အတွက် ဆက်တင်းများဖွင့်ရန် မအောင်မြင်ပါ။"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ဤထည့်သွင်းမှုနည်းလမ်းမှာ သင့်ကိုယ်ရေးအချက်အလက်များဖြစ်သော စကားဝှက်များနှင့် ကရက်ဒစ်ကဒ်နံပါတ်စသည်တို့ကို ရယူသွားမည်ဖြစ်သည်။ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>အပလီကေးရှင်းမှလာပါသည်။ ဤထည့်သွင်းမှုနည်းလမ်းကို အသုံးပြုမည်လား?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"မှတ်ချက် − ပြန်လည်စတင်ပြီးနောက် သင့်ဖုန်းကိုလော့ခ်မဖွင့်မချင်း ဤအက်ပ်ကို အသုံးပြု၍မရပါ"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS မှတ်ပုံတင်ခြင်း အခြေအနေ"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"မှတ်ပုံတင်ထားသည်"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"မှတ်ပုံတင်မထားပါ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"မရရှိနိုင်ပါ။"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 13d27ac..daf0051 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Kunne ikke åpne innstillingene for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Denne inndatametoden har tilgang til all tekst du skriver, blant annet personlige opplysninger som for eksempel passord og kredittkortnumre. Den kommer fra appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vil du bruke denne inndatametoden?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Merk: Etter en omstart kan ikke denne appen starte før du låser opp telefonen din"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Tilstand for IMS-registrering"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrert"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ikke registrert"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ikke tilgjengelig"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 58c5ffc..105c442 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>का लागि सेटिङहरू खोल्न विफल भयो।"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"यस इनपुट विधिले तपाईँले टाइप गर्नुहुने सम्पूर्ण पाठ बटु्ल्न सक्छ, व्यक्तिगत डेटा जस्तै पासवर्ड र क्रेडिट कार्ड नम्बर लगायतका। यो <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अनुप्रयोगबाट आउँदछ। यो इनपुट विधि प्रयोग गर्ने हो?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टिपोट: पुनःबुट पछि तपाईँले आफ्नो फोनलाई अनलक नगरेसम्म यो अनुप्रयोग सुरु हुन सक्दैन"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS दर्ताको स्थिति"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"दर्ता गरिएको"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"दर्ता नगरिएको"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 5cce79e..d8ae320 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Instellingen openen voor <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> mislukt"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Deze invoermethode verzamelt mogelijk alle tekst die je typt, inclusief persoonlijke gegevens zoals wachtwoorden en creditcardnummers. De methode is afkomstig uit de app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Deze invoermethode inschakelen?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Opmerking: Wanneer je telefoon opnieuw is opgestart, kan deze app pas worden gestart nadat je je telefoon hebt ontgrendeld"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registratiestatus"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Geregistreerd"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Niet geregistreerd"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Niet beschikbaar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index becb559..b54d296 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ਲਈ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਵਿੱਚ ਅਸਫਲ"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ਇਹ ਇਨਪੁੱਟ ਵਿਧੀ ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤਾ ਜਾਣ ਵਾਲੀ ਸਾਰੀ ਲਿਖਤ ਇਕੱਤਰ ਕਰਨ ਵਿੱਚ ਸਮਰੱਥ ਹੋ ਸਕਦੀ ਹੈ, ਨਿੱਜੀ ਡਾਟਾ ਸਮੇਤ ਜਿਵੇਂ ਪਾਸਵਰਡ ਅਤੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ। ਇਹ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ਐਪ ਤੋਂ ਆਉਂਦਾ ਹੈ। ਕੀ ਇਹ ਸਪੈੱਲ ਚੈਕਰ ਵਰਤਣਾ ਹੈ?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ਨੋਟ ਕਰੋ: ਰੀਬੂਟ ਤੋਂ ਬਾਅਦ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਆਪਣਾ ਫ਼ੋਨ ਅਣਲਾਕ ਨਹੀਂ ਕਰਦੇ ਤਦ ਤੱਕ ਇਹ ਐਪ ਚਾਲੂ ਨਹੀਂ ਹੋ ਸਕਦੀ"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS ਰਜਿਸਟਰੇਸ਼ਨ ਸਥਿਤੀ"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ਰਜਿਸਟਰ ਕੀਤੀ ਗਈ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ਰਜਿਸਟਰ ਨਹੀਂ ਕੀਤੀ ਗਈ"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ਅਣਉਪਲਬਧ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e3c69fb..4810279 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nie udało się otworzyć ustawień aplikacji <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ta metoda wprowadzania tekstu może gromadzić cały wpisywany tekst, w tym dane osobowe takie jak hasła czy numery kart kredytowych. Pochodzi ona z aplikacji <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Użyć jej?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Uwaga: po restarcie ta aplikacja będzie mogła uruchomić się dopiero po odblokowaniu telefonu"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stan rejestracji IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Zarejestrowane"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Niezarejestrowane"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Niedostępny"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 88c69eb..7a080a2 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as configurações de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada pode coletar todo o texto que você digita, incluindo dados pessoais, como senhas e números de cartão de crédito. É um método do app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Usar este método de entrada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Observação: após uma reinicialização, não é possível iniciar este app até que você desbloqueie seu smartphone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registro de IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registrado"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Não disponível"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index dd73f3c..9229e13 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as definições para <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Este método de introdução pode permitir a recolha de todo o texto que digitar, incluindo dados pessoais como, por exemplo, palavras-passe e números de cartões de crédito. Decorre da aplicação <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizar este método de introdução?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: após reiniciar, só é possível iniciar esta aplicação quando o telemóvel for desbloqueado."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registo IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registado"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Indisponível"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 88c69eb..7a080a2 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as configurações de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada pode coletar todo o texto que você digita, incluindo dados pessoais, como senhas e números de cartão de crédito. É um método do app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Usar este método de entrada?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Observação: após uma reinicialização, não é possível iniciar este app até que você desbloqueie seu smartphone"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registro de IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registrado"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Não disponível"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 93ad315..df34d58 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Deschiderea setărilor pentru <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> a eșuat"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Această metodă de introducere de text poate culege în întregime textul introdus, inclusiv datele personale, cum ar fi parolele și numerele cardurilor de credit. Metoda provine de la aplicația <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizați această metodă de introducere de text?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Notă: după repornire, această aplicație nu poate porni până nu deblocați telefonul"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Situația înregistrării IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Înregistrat"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neînregistrat"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Indisponibilă"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ee5169b..3752cb3 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не удалось открыть настройки приложения \"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>\""</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"При использовании этого способа могут собираться все вводимые данные, в том числе пароли и номера кредитных карт. Запрос поступил от приложения \"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>\". Использовать этот способ ввода?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Примечание. Чтобы запустить это приложение после перезагрузки, разблокируйте экран."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Статус регистрации сервиса IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Зарегистрирован"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зарегистрирован"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 1ce6786..36fd952 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> සඳහා සැකසීම් විවෘත කිරීම අසාර්ථක විය"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"මෙම ආදාන ක්‍රමය මුරපද සහ ණයපත් අංක වැනි පෞද්ගලික දත්ත ඇතුළුව ඔබ ටයිප් කරන පෙළ එකතු කිරීමට හැකිය, එය <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> යෙදුමන් ලැබේ. මෙම ආදාන ක්‍රමය භාවිතා කරන්නද?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"සටහන: නැවත පණ ගැන්වීමකට පසුව, ඔබ ඔබේ දුරකථනය අගුලු හරින තෙක් මෙම යෙදුම ආරම්භ කළ නොහැකිය"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS ලියාපදිංචි තත්ත්වය"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ලියාපදිංචි වී ඇත"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ලියාපදිංචි වී නැත"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ලබාගත නොහැක"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index b1d2fa3..2eea457 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nastavenia aplikácie <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> sa nepodarilo otvoriť"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Pri tejto metóde vstupu sa môže zhromažďovať zadávaný text vrátane osobných údajov, ako sú heslá alebo čísla kreditných kariet. Táto metóda je poskytovaná aplikáciou <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Chcete použiť túto metódu vstupu?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Poznámka: Po reštartovaní sa táto aplikácia spustí až vtedy, keď odomknete telefón"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stav registrácie IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrované"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neregistrované"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nie je k dispozícii"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 21badd6..e7e6212 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ni mogoče odpreti nastavitev za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ta način vnosa lahko morda zbere besedilo, ki ga vtipkate, vključno z osebnimi podatki, kot so gesla in številke kreditnih kartic. Omogoča ga aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Ali želite uporabiti ta način vnosa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Opomba: po vnovičnem zagonu te aplikacije ni mogoče zagnati, če ne odklenete telefona."</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Stanje registracije IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirana"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ni registrirana"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ni na voljo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 54cde3c..f557d81 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Dështoi në hapjen e cilësimeve për <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Kjo metodë hyrjeje mund të mbledhë të gjithë tekstin që shkruan, përfshirë të dhënat personale si fjalëkalimet dhe numrat e kartave të kreditit. Kjo vjen nga aplikacioni <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Të përdoret kjo metodë hyrjeje?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Shënim: Pas një rinisjeje, ky aplikacion nuk mund të niset derisa të shkyçësh telefonin"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Gjendja e regjistrimit të IMS-së"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Regjistruar"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Paregjistruar"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Nuk ofrohet"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4e0bee0..1c0fb24 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Отварање подешавања за апликацију <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> није успело"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Овај метод уноса можда може да прикупља сав текст који уносите, укључујући личне податке, као што су лозинке и бројеви кредитних картица. Потиче од апликације <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Желите ли да користите овај метод уноса?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Напомена: После рестартовања ова апликација не може да се покрене док не откључате телефон"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Статус IMS регистрације"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистрован je"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Није регистрован"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index dea265d..c233bec 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Det gick inte att öppna inställningarna för <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Den här inmatningsmetoden kan samla all text som du skriver, inklusive personliga uppgifter som lösenord och kreditkortsnummer. Den kommer från appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vill du använda inmatningsmetoden?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Obs! När du har startat om enheten måste du låsa upp mobilen innan du kan starta den här appen"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registrering"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrerad"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ej registrerad"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Inte tillgängligt"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 7126594..700153b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Imeshindwa kufungua mipangilio ya <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Huenda mbinu hii ya kuingiza ikakusanya maandishi yote unayoandika, pamoja na data ya kibinafsi kama vile manenosiri na nambari za kadi za mkopo. Inatoka kwa programu <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Je, ungependa kutumia mbinu hii ya kingiza?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Kumbuka: Baada ya kuwasha tena programu hii, hutaweza kuitumia hadi utakapofungua simu yako"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Hali ya usajili wa IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Imesajiliwa"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Haijasajiliwa"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Hapatikani"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 4f563fa..5ea41c4 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> க்கான அமைப்புகளைத் திறப்பதில் தோல்வி"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"இந்த உள்ளீட்டு முறையானது, கடவுச்சொற்கள் மற்றும் கிரெடிட் கார்டு எண்கள் போன்ற தனிப்பட்ட தகவல் உள்பட நீங்கள் உள்ளிடும் எல்லா உரையையும் சேகரிக்கக்கூடும். இது <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> பயன்பாட்டிலிருந்து வந்துள்ளது. இந்த உள்ளீட்டு முறையைப் பயன்படுத்தவா?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"குறிப்பு: மறுதொடக்கம் செய்த பிறகு, மொபைலைத் திறக்கும் வரை இந்தப் பயன்பாட்டால் தொடங்க முடியாது"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS பதிவின் நிலை"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"பதிவு செய்யப்பட்டது"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"பதிவு செய்யப்படவில்லை"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"கிடைக்கவில்லை"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0d1ca375..7daa89e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> యొక్క సెట్టింగ్‌లను తెరవడం విఫలమైంది"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"ఈ ఇన్‌పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ్‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> అనువర్తనంలో అందించబడుతుంది. ఈ ఇన్‌పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"గమనిక: రీబూట్ చేసాక, మీరు మీ ఫోన్‌ను అన్‌లాక్ చేసే వరకు ఈ యాప్ ప్రారంభం కాదు"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS నమోదు స్థితి"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"నమోదు చేయబడింది"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"నమోదు కాలేదు"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"అందుబాటులో లేదు"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index f7ba20a..37fba990 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"ไม่สามารถเปิดการตั้งค่าสำหรับ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"วิธีการป้อนข้อมูลนี้อาจเก็บข้อความทั้งหมดที่คุณพิมพ์ รวมถึงข้อมูลส่วนบุคคล เช่น รหัสผ่านและหมายเลขบัตรเครดิต โดยมาจากแอปพลิเคชัน <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ใช้วิธีการป้อนข้อมูลนี้หรือไม่"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"หมายเหตุ: หลังจากเริ่มต้นใหม่ แอปนี้จะไม่สามารถเริ่มการทำงานได้จนกว่าคุณจะปลดล็อกโทรศัพท์"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"สถานะการลงทะเบียน IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"ลงทะเบียนแล้ว"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ไม่ได้ลงทะเบียน"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"ไม่ว่าง"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 10d8102..25227a3 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nabigong buksan ang mga setting para sa <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Maaaring magawa ng pamamaraan ng pag-input na ito na kolektahin ang lahat ng tekstong iyong tina-type, kabilang ang personal na data katulad ng mga password at mga numero ng credit card. Nagmumula ito sa app na <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gamitin ang pamamaraan ng pag-input na ito?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Tandaan: Pagkatapos ng pag-reboot, hindi makakapagsimula ang app na ito hangga\'t hindi mo ina-unlock ang iyong telepono"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Status ng pagpaparehistro ng IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Nakarehistro"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Hindi nakarehistro"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Hindi available"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 3d8d0d3..893d8b3 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ayarları açılamadı"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Bu giriş yöntemi, şifreler ve kredi kartı numaraları gibi kişisel veriler de dahil olmak üzere yazdığınız tüm metni toplayabilir. Bu yöntem, <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> uygulamasının bir özelliğidir. Bu giriş yöntemini kullanmak istiyor musunuz?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Not: Yeniden başlatma sonrasında, telefonunuzun kilidi açılıncaya kadar bu uygulama başlatılamaz"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS kaydı durumu"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Kaydettirildi"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Kaydettirilmedi"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Kullanılamıyor"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 7d2c6fd..f3ed643 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не вдалося відкрити налаштування для програми <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Під час використання цього методу введення може записуватись весь текст, який ви вводите, зокрема паролі й номери кредитних карток. Цей метод запитує програма <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Використати цей метод введення?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Примітка: щоб запустити цей додаток після перезавантаження, спершу потрібно буде розблокувати телефон"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Статус реєстрації IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Зареєстровано"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зареєстровано"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 63c040e..550280d 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> کیلئے ترتیبات کھولنے میں ناکام ہوگیا"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"اندراج کا یہ طریقہ آپ کے ٹائپ کردہ سبھی متن کو جمع کر سکتا ہے، بشمول ذاتی ڈیٹا جیسے پاس ورڈز اور کریڈٹ کارڈ نمبرز۔ یہ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ایپ سے آتا ہے۔ اندراج کا یہ طریقہ استعمال کریں؟"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"نوٹ: ریبوٹ کرنے کے بعد یہ ایپ تب تک شروع نہیں ہو سکتی جب تک آپ اپنا فون غیر مقفل نہ کر لیں"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"‏IMS رجسٹریشن کی صورتحال"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"رجسٹر شدہ"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"رجسٹر نہیں ہے"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"غیر دستیاب"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index fbe9026..055613b 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> sozlamalarini ochmadi"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Ushbu yozish usuli barcha yozgan matnlaringizni to‘plab olishi mumkin, jumladan kredit karta raqamlari va parollar kabi shaxsiy ma‘lumotlarni ham. Usul  <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ilovasi bilan o‘rnatiladi. Ushbu usuldan foydalanilsinmi?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Eslatma: O‘chirib-yoqilgandan so‘ng, bu ilova to telefoningiz qulfdan chiqarilmaguncha ishga tushmaydi"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS registratsiyasi holati"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Registratsiya qilingan"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Registratsiya qilinmagan"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Mavjud emas"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 07d0999..bc5df8f 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Không thể mở cài đặt cho <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Phương thức nhập này có thể thu thập tất cả văn bản bạn nhập, bao gồm dữ liệu cá nhân như mật khẩu và số thẻ tín dụng. Phương thức nhập này đến từ ứng dụng <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Sử dụng phương thức nhập này?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Lưu ý: Sau khi khởi động lại, ứng dụng này không thể khởi động cho đến khi bạn mở khóa điện thoại"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Trạng thái đăng ký IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Đã đăng ký"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Chưa được đăng ký"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Không có"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 1850f61..4bbc976 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"无法打开 <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 的设置"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"此输入法可能会收集您输入的所有内容,包括密码和信用卡号等个人数据。它来自“<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>”应用。要使用此输入法吗?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新启动后,您必须将手机解锁才能运行此应用"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS 注册状态"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"已注册"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未注册"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"无法获取"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 379a1f1..e70b63b 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"無法開啟 <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 的設定"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"這個輸入法可能會收集您輸入的所有文字,包括密碼和信用卡號碼等個人資料。這個輸入法由 <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> 應用程式提供,您要使用嗎?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新啟動後,您必須解鎖手機,才可開始使用此應用程式"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS 註冊狀態"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"已註冊"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未註冊"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"無法使用"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2abca68..38ec6c7 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"無法開啟「<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>」的設定"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"這個輸入法是由「<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>」應用程式所提供,它可能會收集你輸入的所有文字,包括密碼和信用卡號等個人資料。要啟用這個輸入法嗎?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新啟動後,你必須將手機解鎖,才能執行這個應用程式"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"IMS 註冊狀態"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"已註冊"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未註冊"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"無法取得"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1de1312..1c15d54 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -388,4 +388,8 @@
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Yehlulekile ukuvula izilungiselelo ze-<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
     <string name="ime_security_warning" msgid="4135828934735934248">"Indlela yokufakwayo ingase ikwazi ukuqoqa wonke umbhalo owuthayiphayo, kuhlanganise idatha yomuntu siqu njengamaphasiwedi nezinombolo zekhadi lesikoloto. Iphuma kuhlelo lokusebenza <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Sebenzisa indlela yokufaka?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Yazi: Ngemuva kokuqalisa, lolu hlelo lokusebenza alukwazi ukuqala uze uvule ifoni yakho"</string>
+    <string name="ims_reg_title" msgid="7609782759207241443">"Isimo sokubhaliswa se-IMS"</string>
+    <string name="ims_reg_status_registered" msgid="933003316932739188">"Kubhalisiwe"</string>
+    <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Akubhalisiwe"</string>
+    <string name="status_unavailable" msgid="7862009036663793314">"Ayitholakali"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b728695..3d083b1 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -983,4 +983,14 @@
     <!-- [CHAR LIMIT=NONE] Dialog body explaining that the app just selected by the user will not work after a reboot until until after the user enters their credentials, such as a PIN or password. -->
     <string name="direct_boot_unaware_dialog_message">Note: After a reboot, this app can\'t start until you unlock your phone</string>
 
+    <!--Label of IMS registration header -->
+    <string name="ims_reg_title">"IMS registration state"</string>
+    <!--Used when IMS registration state is registered -->
+    <string name="ims_reg_status_registered">"Registered"</string>
+    <!--Used when IMS registration state is not registered -->
+    <string name="ims_reg_status_not_registered">"Not registered"</string>
+
+    <!-- About phone, status item value if the actual value is not available. -->
+    <string name="status_unavailable">Unavailable</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
index 7998b2e..f79be7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -33,21 +33,27 @@
             + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
     public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
 
+    @VisibleForTesting
+    static final String LOW_RAM_CONFIG_PROPERTY_KEY = "ro.config.low_ram";
     private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size";
     @VisibleForTesting
     static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size";
     static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag";
     // Tricky, isLoggable only checks for first character, assumes silence
     static final String SELECT_LOGD_TAG_SILENCE = "Settings";
-    private static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
+    @VisibleForTesting
+    static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
     private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log";
     private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size";
     @VisibleForTesting
     static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144";
     private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536";
     // 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with.
-    private static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
+    @VisibleForTesting
+    static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
     static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768";
+    @VisibleForTesting
+    static final String DEFAULT_SNET_TAG = "I";
 
     private ListPreference mLogdSize;
 
@@ -154,7 +160,7 @@
             if ((snetValue == null) || (snetValue.length() == 0)) {
                 snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY);
                 if ((snetValue == null) || (snetValue.length() == 0)) {
-                    SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, "I");
+                    SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, DEFAULT_SNET_TAG);
                 }
             }
             // Silence all log sources, security logs notwithstanding
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
new file mode 100644
index 0000000..ba358f8
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * 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.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for bluetooth address
+ */
+public abstract class AbstractBluetoothAddressPreferenceController
+        extends AbstractConnectivityPreferenceController {
+
+    @VisibleForTesting
+    static final String KEY_BT_ADDRESS = "bt_address";
+
+    private static final String[] CONNECTIVITY_INTENTS = {
+            BluetoothAdapter.ACTION_STATE_CHANGED
+    };
+
+    private Preference mBtAddress;
+
+    public AbstractBluetoothAddressPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, lifecycle);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return BluetoothAdapter.getDefaultAdapter() != null;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BT_ADDRESS;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mBtAddress = screen.findPreference(KEY_BT_ADDRESS);
+        updateConnectivity();
+    }
+
+    @Override
+    protected String[] getConnectivityIntents() {
+        return CONNECTIVITY_INTENTS;
+    }
+
+    @SuppressLint("HardwareIds")
+    @Override
+    protected void updateConnectivity() {
+        BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
+        if (bluetooth != null && mBtAddress != null) {
+            String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
+            if (!TextUtils.isEmpty(address)) {
+                // Convert the address to lowercase for consistency with the wifi MAC address.
+                mBtAddress.setSummary(address.toLowerCase());
+            } else {
+                mBtAddress.setSummary(R.string.status_unavailable);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
new file mode 100644
index 0000000..c6552f7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.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.settingslib.deviceinfo;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class for preference controllers which listen to connectivity broadcasts
+ */
+public abstract class AbstractConnectivityPreferenceController
+        extends AbstractPreferenceController implements LifecycleObserver, OnStart, OnStop {
+
+    private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ArrayUtils.contains(getConnectivityIntents(), action)) {
+                getHandler().sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY);
+            }
+        }
+    };
+
+    private static final int EVENT_UPDATE_CONNECTIVITY = 600;
+
+    private Handler mHandler;
+
+    public AbstractConnectivityPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void onStop() {
+        mContext.unregisterReceiver(mConnectivityReceiver);
+    }
+
+    @Override
+    public void onStart() {
+        final IntentFilter connectivityIntentFilter = new IntentFilter();
+        final String[] intents = getConnectivityIntents();
+        for (String intent : intents) {
+            connectivityIntentFilter.addAction(intent);
+        }
+
+        mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter,
+                android.Manifest.permission.CHANGE_NETWORK_STATE, null);
+    }
+
+    protected abstract String[] getConnectivityIntents();
+
+    protected abstract void updateConnectivity();
+
+    private Handler getHandler() {
+        if (mHandler == null) {
+            mHandler = new ConnectivityEventHandler(this);
+        }
+        return mHandler;
+    }
+
+    private static class ConnectivityEventHandler extends Handler {
+        private WeakReference<AbstractConnectivityPreferenceController> mPreferenceController;
+
+        public ConnectivityEventHandler(AbstractConnectivityPreferenceController activity) {
+            mPreferenceController = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            AbstractConnectivityPreferenceController preferenceController
+                    = mPreferenceController.get();
+            if (preferenceController == null) {
+                return;
+            }
+
+            switch (msg.what) {
+                case EVENT_UPDATE_CONNECTIVITY:
+                    preferenceController.updateConnectivity();
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown message " + msg.what);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
new file mode 100644
index 0000000..bb8404b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.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.settingslib.deviceinfo;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.PersistableBundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for IMS status
+ */
+public abstract class AbstractImsStatusPreferenceController
+        extends AbstractConnectivityPreferenceController {
+
+    @VisibleForTesting
+    static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state";
+
+    private static final String[] CONNECTIVITY_INTENTS = {
+            BluetoothAdapter.ACTION_STATE_CHANGED,
+            ConnectivityManager.CONNECTIVITY_ACTION,
+            WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+            WifiManager.NETWORK_STATE_CHANGED_ACTION,
+    };
+
+    private Preference mImsStatus;
+
+    public AbstractImsStatusPreferenceController(Context context,
+            Lifecycle lifecycle) {
+        super(context, lifecycle);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        PersistableBundle config = null;
+        if (configManager != null) {
+            config = configManager.getConfigForSubId(subId);
+        }
+        return config != null && config.getBoolean(
+                CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_IMS_REGISTRATION_STATE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mImsStatus = screen.findPreference(KEY_IMS_REGISTRATION_STATE);
+        updateConnectivity();
+    }
+
+    @Override
+    protected String[] getConnectivityIntents() {
+        return CONNECTIVITY_INTENTS;
+    }
+
+    @Override
+    protected void updateConnectivity() {
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        if (mImsStatus != null) {
+            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ?
+                    R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
new file mode 100644
index 0000000..ded3022
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
@@ -0,0 +1,112 @@
+/*
+ * 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.deviceinfo;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.net.InetAddress;
+import java.util.Iterator;
+
+/**
+ * Preference controller for IP address
+ */
+public abstract class AbstractIpAddressPreferenceController
+        extends AbstractConnectivityPreferenceController {
+
+    @VisibleForTesting
+    static final String KEY_IP_ADDRESS = "wifi_ip_address";
+
+    private static final String[] CONNECTIVITY_INTENTS = {
+            ConnectivityManager.CONNECTIVITY_ACTION,
+            WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+            WifiManager.NETWORK_STATE_CHANGED_ACTION,
+    };
+
+    private Preference mIpAddress;
+    private final ConnectivityManager mCM;
+
+    public AbstractIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, lifecycle);
+        mCM = context.getSystemService(ConnectivityManager.class);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_IP_ADDRESS;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mIpAddress = screen.findPreference(KEY_IP_ADDRESS);
+        updateConnectivity();
+    }
+
+    @Override
+    protected String[] getConnectivityIntents() {
+        return CONNECTIVITY_INTENTS;
+    }
+
+    @Override
+    protected void updateConnectivity() {
+        String ipAddress = getDefaultIpAddresses(mCM);
+        if (ipAddress != null) {
+            mIpAddress.setSummary(ipAddress);
+        } else {
+            mIpAddress.setSummary(R.string.status_unavailable);
+        }
+    }
+
+    /**
+     * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
+     * addresses.
+     * @param cm ConnectivityManager
+     * @return the formatted and newline-separated IP addresses, or null if none.
+     */
+    private static String getDefaultIpAddresses(ConnectivityManager cm) {
+        LinkProperties prop = cm.getActiveLinkProperties();
+        return formatIpAddresses(prop);
+    }
+
+    private static String formatIpAddresses(LinkProperties prop) {
+        if (prop == null) return null;
+        Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+        // If there are no entries, return null
+        if (!iter.hasNext()) return null;
+        // Concatenate all available addresses, newline separated
+        StringBuilder addresses = new StringBuilder();
+        while (iter.hasNext()) {
+            addresses.append(iter.next().getHostAddress());
+            if (iter.hasNext()) addresses.append("\n");
+        }
+        return addresses.toString();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
new file mode 100644
index 0000000..ac61ade
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * 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.deviceinfo;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Preference controller for uptime
+ */
+public abstract class AbstractUptimePreferenceController extends AbstractPreferenceController
+        implements LifecycleObserver, OnStart, OnStop {
+
+    @VisibleForTesting
+    static final String KEY_UPTIME = "up_time";
+    private static final int EVENT_UPDATE_STATS = 500;
+
+    private Preference mUptime;
+    private Handler mHandler;
+
+    public AbstractUptimePreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        getHandler().sendEmptyMessage(EVENT_UPDATE_STATS);
+    }
+
+    @Override
+    public void onStop() {
+        getHandler().removeMessages(EVENT_UPDATE_STATS);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_UPTIME;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mUptime = screen.findPreference(KEY_UPTIME);
+        updateTimes();
+    }
+
+    private Handler getHandler() {
+        if (mHandler == null) {
+            mHandler = new MyHandler(this);
+        }
+        return mHandler;
+    }
+
+    private void updateTimes() {
+        mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+    }
+
+    private static class MyHandler extends Handler {
+        private WeakReference<AbstractUptimePreferenceController> mStatus;
+
+        public MyHandler(AbstractUptimePreferenceController activity) {
+            mStatus = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            AbstractUptimePreferenceController status = mStatus.get();
+            if (status == null) {
+                return;
+            }
+
+            switch (msg.what) {
+                case EVENT_UPDATE_STATS:
+                    status.updateTimes();
+                    sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
+                    break;
+
+                default:
+                    throw new IllegalStateException("Unknown message " + msg.what);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
new file mode 100644
index 0000000..d57b64f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.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 com.android.settingslib.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for WIFI MAC address
+ */
+public abstract class AbstractWifiMacAddressPreferenceController
+        extends AbstractConnectivityPreferenceController {
+
+    @VisibleForTesting
+    static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
+
+    private static final String[] CONNECTIVITY_INTENTS = {
+            ConnectivityManager.CONNECTIVITY_ACTION,
+            WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+            WifiManager.NETWORK_STATE_CHANGED_ACTION,
+    };
+
+    private Preference mWifiMacAddress;
+    private final WifiManager mWifiManager;
+
+    public AbstractWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, lifecycle);
+        mWifiManager = context.getSystemService(WifiManager.class);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_WIFI_MAC_ADDRESS;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
+        updateConnectivity();
+    }
+
+    @Override
+    protected String[] getConnectivityIntents() {
+        return CONNECTIVITY_INTENTS;
+    }
+
+    @SuppressLint("HardwareIds")
+    @Override
+    protected void updateConnectivity() {
+        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+        if (!TextUtils.isEmpty(macAddress)) {
+            mWifiMacAddress.setSummary(macAddress);
+        } else {
+            mWifiMacAddress.setSummary(R.string.status_unavailable);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index 56b8441..9c34763 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -261,7 +261,7 @@
         if (requiredAccountType == null) {
             return true;
         }
-        AccountManager accountManager = AccountManager.get(mContext);
+        AccountManager accountManager = mContext.getSystemService(AccountManager.class);
         Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
         boolean satisfiesRequiredAccount = accounts.length > 0;
         if (!satisfiesRequiredAccount) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 12455d8..c56e1da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -291,15 +291,6 @@
     }
 
     /**
-     * Force a scan for wifi networks to happen now.
-     */
-    public void forceScan() {
-        if (mWifiManager.isWifiEnabled() && mScanner != null) {
-            mScanner.forceScan();
-        }
-    }
-
-    /**
      * Temporarily stop scanning for wifi networks.
      */
     public void pauseScanning() {
@@ -789,14 +780,6 @@
         }
     }
 
-    public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
-            boolean includeScans) {
-        WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
-        tracker.forceUpdate();
-        tracker.copyAndNotifyListeners(false /*notifyListeners*/);
-        return tracker.getAccessPoints();
-    }
-
     @VisibleForTesting
     final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -986,11 +969,6 @@
             }
         }
 
-        void forceScan() {
-            removeMessages(MSG_SCAN);
-            sendEmptyMessage(MSG_SCAN);
-        }
-
         void pause() {
             mRetry = 0;
             removeMessages(MSG_SCAN);
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index 55b635e..bc1a834 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -42,11 +42,12 @@
 # Include the testing libraries (JUnit4 + Robolectric libs).
 LOCAL_STATIC_JAVA_LIBRARIES := \
     mockito-robolectric-prebuilt \
+    platform-robolectric-android-all-stubs \
     truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := \
     junit \
-    platform-robolectric-prebuilt
+    platform-robolectric-3.4.2-prebuilt
 
 LOCAL_INSTRUMENTATION_FOR := SettingsLibShell
 LOCAL_MODULE := SettingsLibRoboTests
@@ -69,4 +70,6 @@
 
 LOCAL_TEST_PACKAGE := SettingsLibShell
 
-include prebuilts/misc/common/robolectric/run_robotests.mk
+LOCAL_ROBOTEST_TIMEOUT := 36000
+
+include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index c3a505a..ca366ea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -20,8 +20,11 @@
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -56,10 +59,10 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private RestrictedLockUtils.Proxy mProxy;
 
-    private static final int mUserId = 194;
-    private static final int mProfileId = 160;
-    private static final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
-    private static final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
+    private final int mUserId = 194;
+    private final int mProfileId = 160;
+    private final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
+    private final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
 
     @Before
     public void setUp() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index 8099eb1..698e442 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -53,15 +53,15 @@
 
     static void getIncludedResourcePaths(String packageName, List<ResourcePath> paths) {
         paths.add(new ResourcePath(
-                packageName,
+                null,
                 Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
                 null));
         paths.add(new ResourcePath(
-                packageName,
+                null,
                 Fs.fileFromPath("./frameworks/base/core/res/res"),
                 null));
         paths.add(new ResourcePath(
-                packageName,
+                null,
                 Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
                 null));
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
index 31abecd..3af9768 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
@@ -17,7 +17,7 @@
 package com.android.settingslib;
 
 public class TestConfig {
-    public static final int SDK_VERSION = 23;
+    public static final int SDK_VERSION = 25;
     public static final String MANIFEST_PATH =
             "frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml";
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index d6bca0e..1290391 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -35,9 +35,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.android.controller.FragmentController;
 import org.robolectric.annotation.Config;
-import org.robolectric.util.ActivityController;
-import org.robolectric.util.FragmentController;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -168,7 +168,7 @@
                 Robolectric.buildFragment(TestDialogFragment.class);
         TestDialogFragment fragment = fragmentController.get();
 
-        fragmentController.attach().create().start().resume();
+        fragmentController.create().start().resume();
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
@@ -192,7 +192,7 @@
                 Robolectric.buildFragment(TestFragment.class);
         TestFragment fragment = fragmentController.get();
 
-        fragmentController.attach().create().start().resume();
+        fragmentController.create().start().resume();
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
@@ -235,7 +235,7 @@
         OptionItemAccepter accepter = new OptionItemAccepter();
         fragment.getLifecycle().addObserver(accepter);
 
-        fragmentController.attach().create().start().resume();
+        fragmentController.create().start().resume();
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 94bfd8f..26d3570 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -16,9 +16,29 @@
 
 package com.android.settingslib.development;
 
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .DEFAULT_SNET_TAG;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .LOW_RAM_CONFIG_PROPERTY_KEY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_MINIMUM_SIZE_VALUE;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_OFF_SIZE_MARKER_VALUE;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_SIZE_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_SNET_TAG_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_TAG_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+        .SELECT_LOGD_TAG_SILENCE;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.os.SystemProperties;
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.PreferenceScreen;
@@ -27,6 +47,7 @@
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.TestConfig;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,22 +66,43 @@
     @Mock
     private PreferenceScreen mPreferenceScreen;
 
+    /**
+     * List Values
+     *
+     * 0: off
+     * 1: 64k
+     * 2: 256k
+     * 3: 1M
+     * 4: 4M
+     * 5: 16M
+     */
+    private String[] mListValues;
+    private String[] mListSummaries;
+    private Context mContext;
     private AbstractLogdSizePreferenceController mController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {};
-
+        mContext = RuntimeEnvironment.application;
+        mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {
+        };
+        mListValues = mContext.getResources().getStringArray(R.array.select_logd_size_values);
+        mListSummaries = mContext.getResources().getStringArray(R.array.select_logd_size_summaries);
         doReturn(mListPreference).when(mPreferenceScreen)
                 .findPreference(mController.getPreferenceKey());
 
         mController.displayPreference(mPreferenceScreen);
     }
 
+    @After
+    public void tearDown() {
+        SystemPropertiesTestImpl.clear();
+    }
+
     @Test
-    public void testUpateLogdSizeValues_lowRamEntries() {
-        SystemProperties.set("ro.config.low_ram", "true");
+    public void testUpdateLogdSizeValues_lowRamEntries() {
+        SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, "true");
         mController.updateLogdSizeValues();
         verify(mListPreference).setEntries(R.array.select_logd_size_lowram_titles);
     }
@@ -77,4 +119,79 @@
         verify(mListPreference).setValue(
                 AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
     }
+
+    @Test
+    public void onPreferenceChange_noTagsSizeValueOff_shouldSetTagAndSnetTagAndSet64KSize() {
+        mController.onPreferenceChange(mListPreference, SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
+
+        final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+        final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+        final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+
+        assertThat(tag).isEqualTo(SELECT_LOGD_TAG_SILENCE);
+        assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
+        assertThat(snetTag).isEqualTo(DEFAULT_SNET_TAG);
+    }
+
+    @Test
+    public void onPreferenceChange_noTagsSizeValue64K_shouldNotSetTagAndSet64KSize() {
+        mController.onPreferenceChange(mListPreference, SELECT_LOGD_MINIMUM_SIZE_VALUE);
+
+        final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+        final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+        final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+
+        assertThat(tag).isEmpty();
+        assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
+        assertThat(snetTag).isEmpty();
+    }
+
+    @Test
+    public void onPreferenceChange_set1M_shouldUpdateSettingLogSizeTo1M() {
+        mController.onPreferenceChange(mListPreference, mListValues[3]);
+
+        final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+
+        assertThat(logSize).isEqualTo(mListValues[3]);
+    }
+
+    @Test
+    public void onPreferenceChange_noValue_shouldUpdateSettingToEmpty() {
+        mController.onPreferenceChange(mListPreference, "" /* new value */);
+
+        final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+
+        assertThat(logSize).isEmpty();
+    }
+
+    @Test
+    public void updateLogdSizeValues_noValueSet_shouldSetDefaultTo64K() {
+        SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
+
+        mController.updateLogdSizeValues();
+
+        verify(mListPreference).setValue(mListValues[2]);
+        verify(mListPreference).setSummary(mListSummaries[2]);
+    }
+
+    @Test
+    public void updateLogdSizeValues_noValueSetLowRamSet_shouldSetDefaultTo64K() {
+        SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, Boolean.toString(true));
+        SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
+
+        mController.updateLogdSizeValues();
+
+        verify(mListPreference).setValue(mListValues[1]);
+        verify(mListPreference).setSummary(mListSummaries[1]);
+    }
+
+    @Test
+    public void updateLogdSizeValues_64KSet_shouldSet64K() {
+        SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, mListValues[1]);
+
+        mController.updateLogdSizeValues();
+
+        verify(mListPreference).setValue(mListValues[1]);
+        verify(mListPreference).setSummary(mListSummaries[1]);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
index 2f89d86..6977e09 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
@@ -54,4 +54,8 @@
     public static void set(String key, String val) {
         sProperties.put(key, val);
     }
+
+    public static synchronized void clear() {
+        sProperties.clear();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..1de7a7a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothAddressPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mPreference).when(mScreen)
+                .findPreference(AbstractBluetoothAddressPreferenceController.KEY_BT_ADDRESS);
+    }
+
+    @Implements(BluetoothAdapter.class)
+    public static class ShadowEmptyBluetoothAdapter {
+        @Implementation
+        public static BluetoothAdapter getDefaultAdapter() {
+            return null;
+        }
+    }
+
+    @Test
+    @Config(shadows = ShadowEmptyBluetoothAdapter.class)
+    public void testNoBluetooth() {
+        final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+                new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+        assertWithMessage("Should not show pref if no bluetooth")
+                .that(bluetoothAddressPreferenceController.isAvailable())
+                .isFalse();
+    }
+
+    @Test
+    public void testHasBluetooth() {
+        final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+                new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+        assertWithMessage("Should show pref if bluetooth is present")
+                .that(bluetoothAddressPreferenceController.isAvailable())
+                .isTrue();
+    }
+
+    @Test
+    public void testHasBluetoothStateChangedFilter() {
+        final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+                new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+        assertWithMessage("Filter should have BluetoothAdapter.ACTION_STATE_CHANGED")
+                .that(bluetoothAddressPreferenceController.getConnectivityIntents())
+                .asList().contains(BluetoothAdapter.ACTION_STATE_CHANGED);
+    }
+
+    private static class ConcreteBluetoothAddressPreferenceController
+            extends AbstractBluetoothAddressPreferenceController {
+
+        public ConcreteBluetoothAddressPreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
new file mode 100644
index 0000000..362dbd9
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.os.Handler;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ConnectivityPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private Lifecycle mLifecycle;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testBroadcastReceiver() {
+        final AbstractConnectivityPreferenceController preferenceController =
+                spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
+
+        final ArgumentCaptor<BroadcastReceiver> receiverArgumentCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        final ArgumentCaptor<IntentFilter> filterArgumentCaptor =
+                ArgumentCaptor.forClass(IntentFilter.class);
+
+        doReturn(new String[] {"Filter1", "Filter2"})
+                .when(preferenceController).getConnectivityIntents();
+
+        preferenceController.onStart();
+
+        verify(mContext, times(1))
+                .registerReceiver(receiverArgumentCaptor.capture(),
+                        filterArgumentCaptor.capture(),
+                        anyString(), nullable(Handler.class));
+
+        final BroadcastReceiver receiver = receiverArgumentCaptor.getValue();
+        final IntentFilter filter = filterArgumentCaptor.getValue();
+
+        assertWithMessage("intent filter should match 'Filter1'")
+                .that(filter.matchAction("Filter1"))
+                .isTrue();
+        assertWithMessage("intent filter should match 'Filter2'")
+                .that(filter.matchAction("Filter2"))
+                .isTrue();
+
+        preferenceController.onStop();
+
+        verify(mContext, times(1)).unregisterReceiver(receiver);
+    }
+
+    private static class ConcreteConnectivityPreferenceController
+            extends AbstractConnectivityPreferenceController {
+
+
+        public ConcreteConnectivityPreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public String getPreferenceKey() {
+            return null;
+        }
+
+        @Override
+        protected String[] getConnectivityIntents() {
+            return new String[0];
+        }
+
+        @Override
+        protected void updateConnectivity() {
+
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
new file mode 100644
index 0000000..112ee64
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ImsStatusPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mPreference).when(mScreen)
+                .findPreference(AbstractImsStatusPreferenceController.KEY_IMS_REGISTRATION_STATE);
+    }
+
+    @Test
+    @Config(shadows = ShadowSubscriptionManager.class)
+    public void testIsAvailable() {
+        CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
+        doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
+
+        PersistableBundle config = new PersistableBundle(1);
+        config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
+        doReturn(config).when(carrierConfigManager).getConfigForSubId(anyInt());
+
+        final AbstractImsStatusPreferenceController imsStatusPreferenceController =
+                new ConcreteImsStatusPreferenceController(mContext, mLifecycle);
+
+        assertWithMessage("Should be available when IMS registration is true").that(
+                imsStatusPreferenceController.isAvailable()).isTrue();
+
+        config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+
+        assertWithMessage("Should not be available when IMS registration is false")
+                .that(imsStatusPreferenceController.isAvailable()).isFalse();
+
+        doReturn(null).when(carrierConfigManager).getConfigForSubId(anyInt());
+
+        assertWithMessage("Should not be available when IMS registration is false")
+                .that(imsStatusPreferenceController.isAvailable()).isFalse();
+
+        doReturn(null).when(mContext).getSystemService(CarrierConfigManager.class);
+
+        assertWithMessage("Should not be available when CarrierConfigManager service is null")
+                .that(imsStatusPreferenceController.isAvailable()).isFalse();
+    }
+
+    @Implements(SubscriptionManager.class)
+    public static class ShadowSubscriptionManager {
+        @Implementation
+        public static int getDefaultDataSubscriptionId() {
+            return 1234;
+        }
+    }
+
+    private static class ConcreteImsStatusPreferenceController
+            extends AbstractImsStatusPreferenceController {
+
+        public ConcreteImsStatusPreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..d0ecae3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class IpAddressPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mPreference).when(mScreen)
+                .findPreference(AbstractIpAddressPreferenceController.KEY_IP_ADDRESS);
+    }
+
+    @Test
+    public void testHasIntentFilters() {
+        final AbstractIpAddressPreferenceController ipAddressPreferenceController =
+                new ConcreteIpAddressPreferenceController(mContext, mLifecycle);
+        final List<String> expectedIntents = Arrays.asList(
+                ConnectivityManager.CONNECTIVITY_ACTION,
+                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+                WifiManager.NETWORK_STATE_CHANGED_ACTION);
+
+
+        assertWithMessage("Intent filter should contain expected intents")
+                .that(ipAddressPreferenceController.getConnectivityIntents())
+                .asList().containsAllIn(expectedIntents);
+    }
+
+    private static class ConcreteIpAddressPreferenceController extends
+            AbstractIpAddressPreferenceController {
+
+        public ConcreteIpAddressPreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
new file mode 100644
index 0000000..f68533b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.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.settingslib.deviceinfo;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UptimePreferenceControllerTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mPreference).when(mScreen)
+                .findPreference(AbstractUptimePreferenceController.KEY_UPTIME);
+    }
+
+    @Test
+    public void testDisplayPreference() {
+        final AbstractUptimePreferenceController uptimePreferenceController =
+                new ConcreteUptimePreferenceController(mContext, mLifecycle);
+
+        uptimePreferenceController.displayPreference(mScreen);
+
+        // SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running
+        verify(mPreference).setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+    }
+
+    @Test
+    public void testUptimeTick() {
+        final AbstractUptimePreferenceController uptimePreferenceController =
+                new ConcreteUptimePreferenceController(mContext, null /* lifecycle */);
+
+        uptimePreferenceController.displayPreference(mScreen);
+
+        verify(mPreference, times(1)).setSummary(any(CharSequence.class));
+
+        uptimePreferenceController.onStart();
+
+        verify(mPreference, times(2)).setSummary(any(CharSequence.class));
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+        verify(mPreference, times(3)).setSummary(any(CharSequence.class));
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+        verify(mPreference, times(4)).setSummary(any(CharSequence.class));
+    }
+
+    private static class ConcreteUptimePreferenceController
+            extends AbstractUptimePreferenceController {
+        public ConcreteUptimePreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+    }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..265a60b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SuppressLint("HardwareIds")
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiMacAddressPreferenceControllerTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mPreference).when(mScreen)
+                .findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS);
+    }
+
+    @Test
+    public void testHasIntentFilters() {
+        final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
+                new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
+        final List<String> expectedIntents = Arrays.asList(
+                ConnectivityManager.CONNECTIVITY_ACTION,
+                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+                WifiManager.NETWORK_STATE_CHANGED_ACTION);
+
+
+        assertWithMessage("Intent filter should contain expected intents")
+                .that(wifiMacAddressPreferenceController.getConnectivityIntents())
+                .asList().containsAllIn(expectedIntents);
+    }
+
+    @Test
+    public void testWifiMacAddress() {
+        final WifiManager wifiManager = mock(WifiManager.class);
+        final WifiInfo wifiInfo = mock(WifiInfo.class);
+        doReturn("00:11:22:33:44:55").when(wifiInfo).getMacAddress();
+
+        doReturn(null).when(wifiManager).getConnectionInfo();
+        doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class);
+
+        final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
+                new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
+
+        wifiMacAddressPreferenceController.displayPreference(mScreen);
+
+        verify(mPreference).setSummary(R.string.status_unavailable);
+
+        doReturn(wifiInfo).when(wifiManager).getConnectionInfo();
+
+        wifiMacAddressPreferenceController.displayPreference(mScreen);
+
+        verify(mPreference).setSummary("00:11:22:33:44:55");
+    }
+
+    private static class ConcreteWifiMacAddressPreferenceController
+            extends AbstractWifiMacAddressPreferenceController {
+
+        public ConcreteWifiMacAddressPreferenceController(Context context,
+                Lifecycle lifecycle) {
+            super(context, lifecycle);
+        }
+    }
+}
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 3e90435..dad3a28 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
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.shadow.api.Shadow.extract;
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -66,7 +67,6 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
-import org.robolectric.internal.ShadowExtractor;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -79,7 +79,6 @@
         shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class})
 public class TileUtilsTest {
 
-    @Mock
     private Context mContext;
     @Mock
     private PackageManager mPackageManager;
@@ -97,6 +96,7 @@
 
     @Before
     public void setUp() throws NameNotFoundException {
+        mContext = spy(application);
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources);
@@ -456,8 +456,7 @@
         assertThat(tile.remoteViews).isNotNull();
         assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
         // Make sure the summary TextView got a new text string.
-        TileUtilsShadowRemoteViews shadowRemoteViews =
-                (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+        TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
         assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
         assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
     }
@@ -494,8 +493,7 @@
         assertThat(tile.remoteViews).isNotNull();
         assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
         // Make sure the summary TextView didn't get any text view updates.
-        TileUtilsShadowRemoteViews shadowRemoteViews =
-                (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+        TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
         assertThat(shadowRemoteViews.overrideViewId).isNull();
         assertThat(shadowRemoteViews.overrideText).isNull();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
index f31d2e1..db599a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
@@ -18,8 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.app.ApplicationPackageManager;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ResolveInfo;
@@ -34,22 +37,21 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.res.ResourceLoader;
-import org.robolectric.res.builder.DefaultPackageManager;
-import org.robolectric.res.builder.RobolectricPackageManager;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows = SuggestionParserTest.TestPackageManager.class)
 public class SuggestionParserTest {
 
-    private Context mContext;
-    private RobolectricPackageManager mPackageManager;
+    private TestPackageManager mPackageManager;
     private SuggestionParser mSuggestionParser;
     private SuggestionCategory mMultipleCategory;
     private SuggestionCategory mExclusiveCategory;
@@ -61,11 +63,8 @@
 
     @Before
     public void setUp() {
-        RuntimeEnvironment.setRobolectricPackageManager(
-                new TestPackageManager(RuntimeEnvironment.getAppResourceLoader()));
-        mContext = RuntimeEnvironment.application;
-        mPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        mPackageManager = extract(application.getPackageManager());
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
         mSuggestion = new Tile();
         mSuggestion.intent = new Intent("action");
         mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
@@ -81,7 +80,7 @@
         mExpiredExclusiveCategory.exclusive = true;
         mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
 
-        mSuggestionParser = new SuggestionParser(mContext, mPrefs,
+        mSuggestionParser = new SuggestionParser(application, mPrefs,
                 Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
                 "0");
 
@@ -199,7 +198,7 @@
 
         final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
         if (mSuggestionParser.dismissSuggestion(suggestion)) {
-            RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
+            mPackageManager.removeResolveInfosForIntent(
                     new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
                     suggestion.intent.getComponent().getPackageName());
         }
@@ -207,13 +206,10 @@
                 mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
     }
 
-    private static class TestPackageManager extends DefaultPackageManager {
+    @Implements(ApplicationPackageManager.class)
+    public static class TestPackageManager extends ShadowApplicationPackageManager {
 
-        TestPackageManager(ResourceLoader appResourceLoader) {
-            super(appResourceLoader);
-        }
-
-        @Override
+        @Implementation
         public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
             return super.queryIntentActivities(intent, flags);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
index a376dcd..b53cc37 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
@@ -16,7 +16,7 @@
 
 package com.android.settingslib.testutils.shadow;
 
-import static org.robolectric.internal.Shadow.directlyOn;
+import static org.robolectric.shadow.api.Shadow.directlyOn;
 
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
index 1f633da..95ff13b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
@@ -29,6 +29,9 @@
     default void showShutdownUi(boolean isReboot, String reason) {
     }
 
+    default void destroy() {
+    }
+
     @ProvidesInterface(version = GlobalActionsManager.VERSION)
     public interface GlobalActionsManager {
         int VERSION = 1;
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e561239..4bae85f 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -536,8 +536,8 @@
     <string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
     <string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
     <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
-    <string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza Configuració ràpida"</string>
-    <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a Configuració ràpida"</string>
+    <string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza la configuració ràpida"</string>
+    <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a configuració ràpida"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vols activar el Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
@@ -705,7 +705,7 @@
     <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha afegit a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha suprimit"</string>
     <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha mogut a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
-    <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de la configuració ràpida."</string>
+    <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de configuració ràpida."</string>
     <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="dock_forced_resizable" msgid="5914261505436217520">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"L\'aplicació no admet la pantalla dividida."</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 7baa57e..0cb6423 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -57,16 +57,16 @@
     SecurityMode getSecurityMode(int userId) {
         KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
 
-        if (SubscriptionManager.isValidSubscriptionId(
-                monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
-            return SecurityMode.SimPin;
-        }
-
         if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
                 monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
             return SecurityMode.SimPuk;
         }
 
+        if (SubscriptionManager.isValidSubscriptionId(
+                monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
+            return SecurityMode.SimPin;
+        }
+
         final int security = mLockPatternUtils.getActivePasswordQuality(userId);
         switch (security) {
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 4b37715..592dda0 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -83,7 +83,6 @@
 
     private boolean mMenuRowIntercepting;
     private boolean mLongPressSent;
-    private LongPressListener mLongPressListener;
     private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
 
@@ -115,10 +114,6 @@
         mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
     }
 
-    public void setLongPressListener(LongPressListener listener) {
-        mLongPressListener = listener;
-    }
-
     public void setDensityScale(float densityScale) {
         mDensityScale = densityScale;
     }
@@ -257,7 +252,7 @@
         }
     }
 
-    public void removeLongPressCallback() {
+    public void cancelLongPress() {
         if (mWatchLongPress != null) {
             mHandler.removeCallbacks(mWatchLongPress);
             mWatchLongPress = null;
@@ -288,33 +283,27 @@
                     mInitialTouchPos = getPos(ev);
                     mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
                     mTranslation = getTranslation(mCurrView);
-                    if (mLongPressListener != null) {
-                        if (mWatchLongPress == null) {
-                            mWatchLongPress = new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mCurrView != null && !mLongPressSent) {
-                                        mLongPressSent = true;
+                    if (mWatchLongPress == null) {
+                        mWatchLongPress = new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mCurrView != null && !mLongPressSent) {
+                                    mLongPressSent = true;
+                                    mCurrView.getLocationOnScreen(mTmpPos);
+                                    final int x = (int) ev.getRawX() - mTmpPos[0];
+                                    final int y = (int) ev.getRawY() - mTmpPos[1];
+                                    if (mCurrView instanceof ExpandableNotificationRow) {
                                         mCurrView.sendAccessibilityEvent(
                                                 AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                                        mCurrView.getLocationOnScreen(mTmpPos);
-                                        final int x = (int) ev.getRawX() - mTmpPos[0];
-                                        final int y = (int) ev.getRawY() - mTmpPos[1];
-                                        MenuItem menuItem = null;
-                                        if (mCurrView instanceof ExpandableNotificationRow) {
-                                            menuItem = ((ExpandableNotificationRow) mCurrView)
-                                                    .getProvider().getLongpressMenuItem(mContext);
-                                        }
-                                        if (menuItem != null) {
-                                            mLongPressListener.onLongPress(mCurrView, x, y,
-                                                    menuItem);
-                                        }
+                                        ExpandableNotificationRow currRow =
+                                                (ExpandableNotificationRow) mCurrView;
+                                        currRow.doLongClickCallback(x, y);
                                     }
                                 }
-                            };
-                        }
-                        mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
+                            }
+                        };
                     }
+                    mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
                 }
                 break;
 
@@ -331,7 +320,7 @@
                         mDragging = true;
                         mInitialTouchPos = getPos(ev);
                         mTranslation = getTranslation(mCurrView);
-                        removeLongPressCallback();
+                        cancelLongPress();
                     }
                 }
                 break;
@@ -343,7 +332,7 @@
                 mCurrView = null;
                 mLongPressSent = false;
                 mMenuRowIntercepting = false;
-                removeLongPressCallback();
+                cancelLongPress();
                 if (captured) return true;
                 break;
         }
@@ -586,7 +575,7 @@
 
                 // We are not doing anything, make sure the long press callback
                 // is not still ticking like a bomb waiting to go off.
-                removeLongPressCallback();
+                cancelLongPress();
                 return false;
             }
         }
@@ -734,15 +723,4 @@
          */
         float getFalsingThresholdFactor();
     }
-
-    /**
-     * Equivalent to View.OnLongClickListener with coordinates
-     */
-    public interface LongPressListener {
-        /**
-         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
-         * @return whether the longpress was handled
-         */
-        boolean onLongPress(View v, int x, int y, MenuItem item);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index dc626fb..851b78c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -84,9 +84,6 @@
             case DOZE_REQUEST_PULSE:
                 pulseWhileDozing(mMachine.getPulseReason());
                 break;
-            case DOZE_PULSE_DONE:
-                mHost.abortPulsing();
-                break;
             case INITIALIZED:
                 mHost.startDozing();
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 09a08f0..f06cda0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -31,6 +31,7 @@
 
 public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
 
+    private GlobalActions mPlugin;
     private Extension<GlobalActions> mExtension;
     private IStatusBarService mBarService;
 
@@ -41,10 +42,19 @@
         mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class)
                 .withPlugin(GlobalActions.class)
                 .withDefault(() -> new GlobalActionsImpl(mContext))
+                .withCallback(this::onExtensionCallback)
                 .build();
+        mPlugin = mExtension.get();
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
     }
 
+    private void onExtensionCallback(GlobalActions newPlugin) {
+        if (mPlugin != null) {
+            mPlugin.destroy();
+        }
+        mPlugin = newPlugin;
+    }
+
     @Override
     public void handleShowShutdownUi(boolean isReboot, String reason) {
         mExtension.get().showShutdownUi(isReboot, reason);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 189badf..d82f9cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -16,27 +16,6 @@
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
-import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
-import com.android.systemui.Interpolators;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
-import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.WallpaperManager;
@@ -69,7 +48,6 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.MathUtils;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -86,7 +64,23 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.R;
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -96,7 +90,8 @@
  * may show depending on whether the keyguard is showing, and whether the device
  * is provisioned.
  */
-class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
+class GlobalActionsDialog implements DialogInterface.OnDismissListener,
+        DialogInterface.OnClickListener {
 
     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -195,6 +190,14 @@
         }
     }
 
+    /**
+     * Dismiss the global actions dialog, if it's currently shown
+     */
+    public void dismissDialog() {
+        mHandler.removeMessages(MESSAGE_DISMISS);
+        mHandler.sendEmptyMessage(MESSAGE_DISMISS);
+    }
+
     private void awakenIfNecessary() {
         if (mDreamManager != null) {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 2cf230c..3563437 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -14,12 +14,12 @@
 
 package com.android.systemui.globalactions;
 
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+
 import android.app.Dialog;
 import android.app.KeyguardManager;
-import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.Point;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -32,12 +32,14 @@
 import com.android.internal.colorextraction.drawable.GradientDrawable;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
-public class GlobalActionsImpl implements GlobalActions {
+public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
 
     private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
 
@@ -45,15 +47,23 @@
     private final KeyguardMonitor mKeyguardMonitor;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private GlobalActionsDialog mGlobalActions;
+    private boolean mDisabled;
 
     public GlobalActionsImpl(Context context) {
         mContext = context;
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
+    }
+
+    @Override
+    public void destroy() {
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
     }
 
     @Override
     public void showGlobalActions(GlobalActionsManager manager) {
+        if (mDisabled) return;
         if (mGlobalActions == null) {
             mGlobalActions = new GlobalActionsDialog(mContext, manager);
         }
@@ -107,4 +117,14 @@
 
         d.show();
     }
+
+    @Override
+    public void disable(int state1, int state2, boolean animate) {
+        final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+        if (disabled == mDisabled) return;
+        mDisabled = disabled;
+        if (disabled && mGlobalActions != null) {
+            mGlobalActions.dismissDialog();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 86b7790..ed447c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -188,41 +188,6 @@
             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                 // When switching users, dismiss Recents to Home similar to screen off
                 finish();
-            } else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
-                // If the time shifts but the currentTime >= lastStackActiveTime, then that boundary
-                // is still valid.  Otherwise, we need to reset the lastStackactiveTime to the
-                // currentTime and remove the old tasks in between which would not be previously
-                // visible, but currently would be in the new currentTime
-                int currentUser = SystemServicesProxy.getInstance(RecentsActivity.this)
-                        .getCurrentUser();
-                long oldLastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(),
-                        Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1, currentUser);
-                if (oldLastStackActiveTime != -1) {
-                    long currentTime = System.currentTimeMillis();
-                    if (currentTime < oldLastStackActiveTime) {
-                        // We are only removing tasks that are between the new current time
-                        // and the old last stack active time, they were not visible and in the
-                        // TaskStack so we don't need to remove any associated TaskViews but we do
-                        // need to load the task id's from the system
-                        RecentsTaskLoader loader = Recents.getTaskLoader();
-                        RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(ctx);
-                        loader.preloadRawTasks(loadPlan, false /* includeFrontMostExcludedTask */);
-                        List<ActivityManager.RecentTaskInfo> tasks = loadPlan.getRawTasks();
-                        for (int i = tasks.size() - 1; i >= 0; i--) {
-                            ActivityManager.RecentTaskInfo task = tasks.get(i);
-                            if (currentTime <= task.lastActiveTime && task.lastActiveTime <
-                                    oldLastStackActiveTime) {
-                                Recents.getSystemServices().removeTask(task.persistentId);
-                            }
-                        }
-                        Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
-                                currentTime, currentUser);
-
-                        // Clear the last PiP task time, it's an edge case and we'd rather it
-                        // not relaunch the PiP task if the user double taps
-                        RecentsImpl.clearLastPipTime();
-                    }
-                }
             }
         }
     };
@@ -383,7 +348,6 @@
         // Register the broadcast receiver to handle messages when the screen is turned off
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         registerReceiver(mSystemBroadcastReceiver, filter);
 
@@ -467,8 +431,7 @@
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
         if (!loadPlan.hasTasks()) {
-            loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
-                    !launchState.launchedFromHome && !launchState.launchedViaDockGesture);
+            loader.preloadTasks(loadPlan, launchState.launchedToTaskId);
         }
 
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
@@ -889,8 +852,7 @@
         RecentsActivityLaunchState launchState = config.getLaunchState();
         RecentsTaskLoader loader = Recents.getTaskLoader();
         RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
-        loader.preloadTasks(loadPlan, -1 /* runningTaskId */,
-                false /* includeFrontMostExcludedTask */);
+        loader.preloadTasks(loadPlan, -1 /* runningTaskId */);
 
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
         loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
@@ -931,13 +893,9 @@
         Recents.getTaskLoader().dump(prefix, writer);
 
         String id = Integer.toHexString(System.identityHashCode(this));
-        long lastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(),
-                Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1,
-                SystemServicesProxy.getInstance(this).getCurrentUser());
 
         writer.print(prefix); writer.print(TAG);
         writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
-        writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime);
         writer.print(" currentTime="); writer.print(System.currentTimeMillis());
         writer.print(" [0x"); writer.print(id); writer.print("]");
         writer.println();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 3e2a5f3..8dd3bdc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -142,7 +142,7 @@
                 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
                 RecentsTaskLoader loader = Recents.getTaskLoader();
                 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-                loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
+                loader.preloadTasks(plan, -1);
                 TaskStack stack = plan.getTaskStack();
                 RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
                 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
@@ -283,7 +283,7 @@
         // We can use a new plan since the caches will be the same.
         RecentsTaskLoader loader = Recents.getTaskLoader();
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-        loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
+        loader.preloadTasks(plan, -1);
         RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
         launchOpts.numVisibleTasks = loader.getIconCacheSize();
         launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
@@ -473,8 +473,7 @@
         // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
         // don't block the touch feedback on the nav bar button which triggers this.
         mHandler.post(() -> {
-            MutableBoolean isHomeStackVisible = new MutableBoolean(true);
-            if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
+            if (!ssp.isRecentsActivityVisible(null)) {
                 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
                 if (runningTask == null) {
                     return;
@@ -482,7 +481,7 @@
 
                 RecentsTaskLoader loader = Recents.getTaskLoader();
                 sInstanceLoadPlan = loader.createLoadPlan(mContext);
-                loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value);
+                loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
                 TaskStack stack = sInstanceLoadPlan.getTaskStack();
                 if (stack.getTaskCount() > 0) {
                     // Only preload the icon (but not the thumbnail since it may not have been taken
@@ -522,7 +521,7 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         RecentsTaskLoader loader = Recents.getTaskLoader();
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-        loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
+        loader.preloadTasks(plan, -1);
         TaskStack focusedStack = plan.getTaskStack();
 
         // Return early if there are no tasks in the focused stack
@@ -577,7 +576,7 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         RecentsTaskLoader loader = Recents.getTaskLoader();
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-        loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
+        loader.preloadTasks(plan, -1);
         TaskStack focusedStack = plan.getTaskStack();
 
         // Return early if there are no tasks in the focused stack
@@ -1012,7 +1011,7 @@
             sInstanceLoadPlan = loader.createLoadPlan(mContext);
         }
         if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
-            loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible);
+            loader.preloadTasks(sInstanceLoadPlan, runningTaskId);
         }
 
         TaskStack stack = sInstanceLoadPlan.getTaskStack();
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 bddf9a5..bc3adaf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.misc;
 
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -68,7 +69,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.util.ArraySet;
@@ -385,20 +385,15 @@
 
     /**
      * Returns a list of the recents tasks.
-     *
-     * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
-     *                                     will be visible, otherwise no excluded tasks will be
-     *                                     visible.
      */
-    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
-            boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
+    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
         if (mAm == null) return null;
 
         // If we are mocking, then create some recent tasks
         if (RecentsDebugFlags.Static.EnableMockTasks) {
             ArrayList<ActivityManager.RecentTaskInfo> tasks =
                     new ArrayList<ActivityManager.RecentTaskInfo>();
-            int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
+            int count = Math.min(numTasks, RecentsDebugFlags.Static.MockTaskCount);
             for (int i = 0; i < count; i++) {
                 // Create a dummy component name
                 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
@@ -412,7 +407,7 @@
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
                 rti.description = description;
-                rti.firstActiveTime = rti.lastActiveTime = i;
+                rti.lastActiveTime = i;
                 if (i % 2 == 0) {
                     rti.taskDescription = new ActivityManager.TaskDescription(description,
                             Bitmap.createBitmap(mDummyIcon), null,
@@ -427,57 +422,24 @@
             return tasks;
         }
 
-        // Remove home/recents/excluded tasks
-        int minNumTasksToQuery = 10;
-        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
-        int flags = ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS |
-                ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
-                ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
-                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                ActivityManager.RECENT_INCLUDE_PROFILES;
-        if (includeFrontMostExcludedTask) {
-            flags |= ActivityManager.RECENT_WITH_EXCLUDED;
-        }
-        List<ActivityManager.RecentTaskInfo> tasks = null;
         try {
-            tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
+            List<ActivityManager.RecentTaskInfo> tasks = mIam.getRecentTasks(numTasks,
+                    RECENT_IGNORE_UNAVAILABLE, userId).getList();
+            Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
+            while (iter.hasNext()) {
+                ActivityManager.RecentTaskInfo t = iter.next();
+
+                // Remove the task if it or it's package are blacklsited
+                if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
+                        sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
+                    iter.remove();
+                }
+            }
+            return tasks;
         } catch (Exception e) {
             Log.e(TAG, "Failed to get recent tasks", e);
-        }
-
-        // Break early if we can't get a valid set of tasks
-        if (tasks == null) {
             return new ArrayList<>();
         }
-
-        boolean isFirstValidTask = true;
-        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
-        while (iter.hasNext()) {
-            ActivityManager.RecentTaskInfo t = iter.next();
-
-            // NOTE: The order of these checks happens in the expected order of the traversal of the
-            // tasks
-
-            // Remove the task if it or it's package are blacklsited
-            if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
-                    sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
-                iter.remove();
-                continue;
-            }
-
-            // Remove the task if it is marked as excluded, unless it is the first most task and we
-            // are requested to include it
-            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-            isExcluded |= quietProfileIds.contains(t.userId);
-            if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
-                iter.remove();
-            }
-
-            isFirstValidTask = false;
-        }
-
-        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
     }
 
     /**
@@ -1292,14 +1254,6 @@
         });
     }
 
-    public void updateOverviewLastStackActiveTimeAsync(long newLastStackActiveTime,
-            int currentUserId) {
-        mUiOffloadThread.submit(() -> {
-            Settings.Secure.putLongForUser(mContext.getContentResolver(),
-                    Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, newLastStackActiveTime, currentUserId);
-        });
-    }
-
     public interface StartActivityFromRecentsResultListener {
         void onStartActivityResult(boolean succeeded);
     }
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 d5e0313..3a357e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -57,10 +57,6 @@
  */
 public class RecentsTaskLoadPlan {
 
-    private static int MIN_NUM_TASKS = 5;
-    private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
-            6 /* hrs */;
-
     /** The set of conditions to load tasks. */
     public static class Options {
         public int runningTaskId = -1;
@@ -74,44 +70,24 @@
 
     Context mContext;
 
-    int mPreloadedUserId;
     List<ActivityManager.RecentTaskInfo> mRawTasks;
     TaskStack mStack;
-    ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
 
     /** Package level ctor */
     RecentsTaskLoadPlan(Context context) {
         mContext = context;
     }
 
-    private void updateCurrentQuietProfilesCache(int currentUserId) {
-        mCurrentQuietProfiles.clear();
-
-        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        List<UserInfo> profiles = userManager.getProfiles(currentUserId);
-        if (profiles != null) {
-            for (int i = 0; i < profiles.size(); i++) {
-                UserInfo user  = profiles.get(i);
-                if (user.isManagedProfile() && user.isQuietModeEnabled()) {
-                    mCurrentQuietProfiles.add(user.id);
-                }
-            }
-        }
-    }
-
     /**
      * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
      * to most-recent order.
      *
      * Note: Do not lock, callers should synchronize on the loader before making this call.
      */
-    void preloadRawTasks(boolean includeFrontMostExcludedTask) {
+    void preloadRawTasks() {
         SystemServicesProxy ssp = Recents.getSystemServices();
         int currentUserId = ssp.getCurrentUser();
-        updateCurrentQuietProfilesCache(currentUserId);
-        mPreloadedUserId = currentUserId;
-        mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
-                currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
+        mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), currentUserId);
 
         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(mRawTasks);
@@ -129,12 +105,11 @@
      * Note: Do not lock, since this can be calling back to the loader, which separately also drives
      * this call (callers should synchronize on the loader before making this call).
      */
-    void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
-            boolean includeFrontMostExcludedTask) {
+    void preloadPlan(RecentsTaskLoader loader, int runningTaskId) {
         Resources res = mContext.getResources();
         ArrayList<Task> allTasks = new ArrayList<>();
         if (mRawTasks == null) {
-            preloadRawTasks(includeFrontMostExcludedTask);
+            preloadRawTasks();
         }
 
         SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
@@ -144,14 +119,6 @@
                 R.string.accessibility_recents_item_will_be_dismissed);
         String appInfoDescFormat = mContext.getString(
                 R.string.accessibility_recents_item_open_app_info);
-        int currentUserId = mPreloadedUserId;
-        long legacyLastStackActiveTime = migrateLegacyLastStackActiveTime(currentUserId);
-        long lastStackActiveTime = Settings.Secure.getLongForUser(mContext.getContentResolver(),
-                Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, legacyLastStackActiveTime, currentUserId);
-        if (RecentsDebugFlags.Static.EnableMockTasks) {
-            lastStackActiveTime = 0;
-        }
-        long newLastStackActiveTime = -1;
         int taskCount = mRawTasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
@@ -159,35 +126,12 @@
             // Compose the task key
             final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent,
-                    t.userId, t.firstActiveTime, t.lastActiveTime);
+                    t.userId, t.lastActiveTime);
 
-            // This task is only shown in the stack if it satisfies the historical time or min
-            // number of tasks constraints. Freeform tasks are also always shown.
             boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
-            boolean isStackTask;
-            if (Recents.getConfiguration().isGridEnabled) {
-                // When grid layout is enabled, we only show the first
-                // TaskGridLayoutAlgorithm.MAX_LAYOUT_FROM_HOME_TASK_COUNT} tasks.
-                isStackTask = t.lastActiveTime >= lastStackActiveTime &&
-                    i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
-            } else if (Recents.getConfiguration().isLowRamDevice) {
-                // Show a max of 3 items
-                isStackTask = t.lastActiveTime >= lastStackActiveTime &&
-                        i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
-            } else {
-                isStackTask = isFreeformTask || !isHistoricalTask(t) ||
-                    (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
-            }
+            boolean isStackTask = !isFreeformTask;
             boolean isLaunchTarget = taskKey.id == runningTaskId;
 
-            // The last stack active time is the baseline for which we show visible tasks.  Since
-            // the system will store all the tasks, we don't want to show the tasks prior to the
-            // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
-            // the other stack-task constraints.
-            if (isStackTask && newLastStackActiveTime < 0) {
-                newLastStackActiveTime = t.lastActiveTime;
-            }
-
             // Load the title, icon, and color
             ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
@@ -213,17 +157,13 @@
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
-                    t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
-                    isLocked);
+                    t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode,
+                    t.topActivity, isLocked);
 
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
             affiliatedTasks.put(taskKey.id, taskKey);
         }
-        if (newLastStackActiveTime != -1) {
-            Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
-                    newLastStackActiveTime, currentUserId);
-        }
 
         // Initialize the stacks
         mStack = new TaskStack();
@@ -289,42 +229,4 @@
         }
         return false;
     }
-
-    /**
-     * Returns whether this task is too old to be shown.
-     */
-    private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
-        return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
-    }
-
-
-    /**
-     * Migrate the last active time from the prefs to the secure settings.
-     *
-     * The first time this runs, it will:
-     * 1) fetch the last stack active time from the prefs
-     * 2) set the prefs to the last stack active time for all users
-     * 3) clear the pref
-     * 4) return the last stack active time
-     *
-     * Subsequent calls to this will return zero.
-     */
-    private long migrateLegacyLastStackActiveTime(int currentUserId) {
-        long legacyLastStackActiveTime = Prefs.getLong(mContext,
-                Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
-        if (legacyLastStackActiveTime != -1) {
-            Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME);
-            UserManager userMgr = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            List<UserInfo> users = userMgr.getUsers();
-            for (int i = 0; i < users.size(); i++) {
-                int userId = users.get(i).id;
-                if (userId != currentUserId) {
-                    Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
-                            legacyLastStackActiveTime, userId);
-                }
-            }
-            return legacyLastStackActiveTime;
-        }
-        return 0;
-    }
 }
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 1b89386..94558c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -337,18 +337,11 @@
         return plan;
     }
 
-    /** Preloads raw recents tasks using the specified plan to store the output. */
-    public synchronized void preloadRawTasks(RecentsTaskLoadPlan plan,
-            boolean includeFrontMostExcludedTask) {
-        plan.preloadRawTasks(includeFrontMostExcludedTask);
-    }
-
     /** Preloads recents tasks using the specified plan to store the output. */
-    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
-            boolean includeFrontMostExcludedTask) {
+    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
         try {
             Trace.beginSection("preloadPlan");
-            plan.preloadPlan(this, runningTaskId, includeFrontMostExcludedTask);
+            plan.preloadPlan(this, runningTaskId);
         } finally {
             Trace.endSection();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index abdb5cb..b471574 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -69,13 +69,11 @@
 
         private int mHashCode;
 
-        public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime,
-                long lastActiveTime) {
+        public TaskKey(int id, int windowingMode, Intent intent, int userId, long lastActiveTime) {
             this.id = id;
             this.windowingMode = windowingMode;
             this.baseIntent = intent;
             this.userId = userId;
-            this.firstActiveTime = firstActiveTime;
             this.lastActiveTime = lastActiveTime;
             updateHashCode();
         }
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 fdae917..0c8316b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -960,12 +960,6 @@
             ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
             // Sort all tasks by increasing firstActiveTime of the task
             ArrayList<Task> tasks = mStackTaskList.getTasks();
-            Collections.sort(tasks, new Comparator<Task>() {
-                @Override
-                public int compare(Task task, Task task2) {
-                    return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime);
-                }
-            });
             // Create groups when sequential packages are the same
             NamedCounter counter = new NamedCounter("task-group", "");
             int taskCount = tasks.size();
@@ -1006,12 +1000,6 @@
             int groupCount = mGroups.size();
             for (int i = 0; i < groupCount; i++) {
                 TaskGrouping group = mGroups.get(i);
-                Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() {
-                    @Override
-                    public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
-                        return Long.compare(taskKey.firstActiveTime, taskKey2.firstActiveTime);
-                    }
-                });
                 ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys;
                 int groupTaskCount = groupTasks.size();
                 for (int j = 0; j < groupTaskCount; j++) {
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 c7edb9a..e460bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.recents.views;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityOptions.OnAnimationStartedListener;
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 9d63964..032d966 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.recents.views;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 7699bb9..9211e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.shortcut;
 
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.os.UserHandle.USER_CURRENT;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
@@ -102,11 +106,10 @@
                 // If there is no window docked, we dock the top-most window.
                 Recents recents = getComponent(Recents.class);
                 int dockMode = (shortcutCode == SC_DOCK_LEFT)
-                        ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
-                        : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                        ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+                        : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
                 List<ActivityManager.RecentTaskInfo> taskList =
-                        SystemServicesProxy.getInstance(mContext).getRecentTasks(1,
-                                UserHandle.USER_CURRENT, false, new ArraySet<>());
+                        SystemServicesProxy.getInstance(mContext).getRecentTasks(1, USER_CURRENT);
                 recents.showRecentApps(
                         false /* triggeredFromAltTab */,
                         false /* fromHome */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 966e789..6c5f4b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
@@ -174,6 +175,11 @@
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
     private OnExpandClickListener mOnExpandClickListener;
+
+    // Listener will be called when receiving a long click event.
+    // Use #setLongPressPosition to optionally assign positional data with the long press.
+    private LongPressListener mLongPressListener;
+
     private boolean mGroupExpansionChanging;
 
     /**
@@ -788,6 +794,10 @@
         mOnExpandClickListener = onExpandClickListener;
     }
 
+    public void setLongPressListener(LongPressListener longPressListener) {
+        mLongPressListener = longPressListener;
+    }
+
     @Override
     public void setOnClickListener(@Nullable OnClickListener l) {
         super.setOnClickListener(l);
@@ -1338,6 +1348,47 @@
         }
     }
 
+    private void doLongClickCallback() {
+        doLongClickCallback(getWidth() / 2, getHeight() / 2);
+    }
+
+    public void doLongClickCallback(int x, int y) {
+        createMenu();
+        MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
+        if (mLongPressListener != null && menuItem != null) {
+            mLongPressListener.onLongPress(this, x, y, menuItem);
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            event.startTracking();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            if (!event.isCanceled()) {
+                performClick();
+            }
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            doLongClickCallback();
+            return true;
+        }
+        return false;
+    }
+
     public void resetTranslation() {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
@@ -2205,6 +2256,7 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
         if (canViewBeDismissed()) {
             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
         }
@@ -2244,6 +2296,9 @@
             case AccessibilityNodeInfo.ACTION_EXPAND:
                 mExpandClickListener.onClick(this);
                 return true;
+            case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+                doLongClickCallback();
+                return true;
         }
         return false;
     }
@@ -2332,4 +2387,15 @@
     protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
         mChildrenContainer = childrenContainer;
     }
+
+    /**
+     * Equivalent to View.OnLongClickListener with coordinates
+     */
+    public interface LongPressListener {
+        /**
+         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
+         * @return whether the longpress was handled
+         */
+        boolean onLongPress(View v, int x, int y, MenuItem item);
+    }
 }
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 59d3e0a..2ad881f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -44,6 +44,7 @@
 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.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -247,11 +248,12 @@
     }
 
     /**
-     * Returns the {@link com.android.systemui.SwipeHelper.LongPressListener} that will be
-     * triggered when a notification card is long-pressed.
+     * Returns the
+     * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
+     * be triggered when a notification card is long-pressed.
      */
     @Override
-    protected SwipeHelper.LongPressListener getNotificationLongClicker() {
+    protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
         // For the automative use case, we do not want to the user to be able to interact with
         // a notification other than a regular click. As a result, just return null for the
         // long click listener.
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 7b11ace..af03440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -704,7 +704,7 @@
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mQsTracking = true;
                     mIntercepting = false;
-                    mNotificationStackScroller.removeLongPressCallback();
+                    mNotificationStackScroller.cancelLongPress();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -740,7 +740,7 @@
                     mInitialTouchY = y;
                     mInitialTouchX = x;
                     mIntercepting = false;
-                    mNotificationStackScroller.removeLongPressCallback();
+                    mNotificationStackScroller.cancelLongPress();
                     return true;
                 }
                 break;
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 9c837ed..b0ce577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -639,12 +639,17 @@
     }
 
     private Intent getTaskIntent(int taskId, int userId) {
-        List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class)
-                .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId);
-        for (int i = 0; i < tasks.size(); i++) {
-            if (tasks.get(i).id == taskId) {
-                return tasks.get(i).baseIntent;
+        try {
+            final List<ActivityManager.RecentTaskInfo> tasks =
+                    ActivityManager.getService().getRecentTasks(
+                            NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList();
+            for (int i = 0; i < tasks.size(); i++) {
+                if (tasks.get(i).id == taskId) {
+                    return tasks.get(i).baseIntent;
+                }
             }
+        } catch (RemoteException e) {
+            // Fall through
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 54be857..9f03954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -160,7 +160,6 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
-import com.android.systemui.SwipeHelper;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
@@ -4838,7 +4837,7 @@
 
     @Override
     public void onTouchSlopExceeded() {
-        mStackScroller.removeLongPressCallback();
+        mStackScroller.cancelLongPress();
         mStackScroller.checkSnoozeLeavebehind();
     }
 
@@ -5470,7 +5469,7 @@
 
         @Override
         public void onDoubleTap(float screenX, float screenY) {
-            if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null 
+            if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
                 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
                 mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
                 float viewX = screenX - mTmpInt2[0];
@@ -5882,8 +5881,7 @@
                 List<ActivityManager.RecentTaskInfo> recentTask = null;
                 try {
                     recentTask = ActivityManager.getService().getRecentTasks(1,
-                            ActivityManager.RECENT_WITH_EXCLUDED
-                            | ActivityManager.RECENT_INCLUDE_PROFILES,
+                            ActivityManager.RECENT_WITH_EXCLUDED,
                             mCurrentUserId).getList();
                 } catch (RemoteException e) {
                     // Abandon hope activity manager not running.
@@ -6296,14 +6294,15 @@
                 true /* removeControls */, x, y, true /* resetMenu */);
     }
 
-    protected SwipeHelper.LongPressListener getNotificationLongClicker() {
-        return new SwipeHelper.LongPressListener() {
+    protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+        return new ExpandableNotificationRow.LongPressListener() {
             @Override
             public boolean onLongPress(View v, final int x, final int y,
                     MenuItem item) {
                 if (!(v instanceof ExpandableNotificationRow)) {
                     return false;
                 }
+
                 if (v.getWindowToken() == null) {
                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
                     return false;
@@ -6318,7 +6317,7 @@
                     closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
                             true /* removeControls */, -1 /* x */, -1 /* y */,
                             true /* resetMenu */);
-                    return false;
+                    return true;
                 }
                 bindGuts(row, item);
                 NotificationGuts guts = row.getGuts();
@@ -6598,6 +6597,7 @@
         row.setRemoteViewClickHandler(mOnClickHandler);
         row.setInflationCallback(this);
         row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
+        row.setLongPressListener(getNotificationLongClicker());
 
         // Get the app name.
         // Note that Notification.Builder#bindHeaderAppName has similar logic
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 75532d9..1e14626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -240,7 +240,7 @@
      * motion.
      */
     private int mMaxScrollAfterExpand;
-    private SwipeHelper.LongPressListener mLongPressListener;
+    private ExpandableNotificationRow.LongPressListener mLongPressListener;
 
     private NotificationMenuRowPlugin mCurrMenuRow;
     private View mTranslatingParentView;
@@ -410,7 +410,6 @@
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(this);
         mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
-        mSwipeHelper.setLongPressListener(mLongPressListener);
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
         initView(context);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -884,8 +883,7 @@
         return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
-    public void setLongPressListener(SwipeHelper.LongPressListener listener) {
-        mSwipeHelper.setLongPressListener(listener);
+    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
         mLongPressListener = listener;
     }
 
@@ -1175,7 +1173,7 @@
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
         }
-        removeLongPressCallback();
+        cancelLongPress();
         requestDisallowInterceptTouchEvent(true);
     }
 
@@ -2581,7 +2579,7 @@
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
         if (disallowIntercept) {
-            mSwipeHelper.removeLongPressCallback();
+            cancelLongPress();
         }
     }
 
@@ -3302,7 +3300,7 @@
         mIsBeingDragged = isDragged;
         if (isDragged) {
             requestDisallowInterceptTouchEvent(true);
-            removeLongPressCallback();
+            cancelLongPress();
         }
     }
 
@@ -3310,7 +3308,7 @@
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
         if (!hasWindowFocus) {
-            removeLongPressCallback();
+            cancelLongPress();
         }
     }
 
@@ -3324,7 +3322,7 @@
 
     @Override
     public void requestDisallowLongPress() {
-        removeLongPressCallback();
+        cancelLongPress();
     }
 
     @Override
@@ -3332,8 +3330,8 @@
         mDisallowDismissInThisMotion = true;
     }
 
-    public void removeLongPressCallback() {
-        mSwipeHelper.removeLongPressCallback();
+    public void cancelLongPress() {
+        mSwipeHelper.cancelLongPress();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index b835909..5ec3dff 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@
  */
 public class DelayedWakeLock implements WakeLock {
 
-    private static final long RELEASE_DELAY_MS = 100;
+    private static final long RELEASE_DELAY_MS = 120;
 
     private final Handler mHandler;
     private final WakeLock mInner;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
index 767e124..4564c8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
@@ -60,7 +60,7 @@
         MockitoAnnotations.initMocks(this);
         mLoader = new HighResThumbnailLoader(mMockSystemServicesProxy, Looper.getMainLooper(),
                 false);
-        mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0, 0);
+        mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0);
         when(mMockSystemServicesProxy.getTaskThumbnail(anyInt(), anyBoolean()))
                 .thenReturn(mThumbnailData);
         mLoader.setVisible(true);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index e17a6a6..9b167c8 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4665,6 +4665,28 @@
     // OS: P
     DIALOG_CLEAR_ADB_KEYS = 1223;
 
+    // Open: Settings > Developer options > Quick setting tile config
+    // CATEGORY: SETTINGS
+    // OS: P
+    DEVELOPMENT_QS_TILE_CONFIG = 1224;
+
+    // OPEN: Settings > Developer options > Store logger data persistently on device > Info dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_LOG_PERSIST = 1225;
+
+    // ACTION: DND Settings > Priority only allows > Alarms toggle
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ALLOW_ALARMS = 1162;
+
+    // ACTION: DND Settings > Priority only allows > Media toggle
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ALLOW_MEDIA = 1163;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a6aaaa67..51afada 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -71,7 +71,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.service.appwidget.AppWidgetServiceDumpProto;
 import android.service.appwidget.WidgetProto;
 import android.text.TextUtils;
@@ -159,7 +158,9 @@
     // Bump if the stored widgets need to be upgraded.
     private static final int CURRENT_VERSION = 1;
 
-    private static final AtomicLong REQUEST_COUNTER = new AtomicLong();
+    // Every widget update request is associated which an increasing sequence number. This is
+    // used to verify which request has successfully been received by the host.
+    private static final AtomicLong UPDATE_COUNTER = new AtomicLong();
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -814,9 +815,9 @@
             Host host = lookupOrAddHostLocked(id);
             host.callbacks = callbacks;
 
+            long updateSequenceNo = UPDATE_COUNTER.incrementAndGet();
             int N = appWidgetIds.length;
             ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N);
-
             LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
             for (int i = 0; i < N; i++) {
                 if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
@@ -828,6 +829,8 @@
                     }
                 }
             }
+            // Reset the update counter once all the updates have been calculated
+            host.lastWidgetUpdateSequenceNo = updateSequenceNo;
             return new ParceledListSlice<>(outUpdates);
         }
     }
@@ -1914,9 +1917,9 @@
             // method with a wrong id. In that case, ignore the call.
             return;
         }
-        long requestId = REQUEST_COUNTER.incrementAndGet();
+        long requestId = UPDATE_COUNTER.incrementAndGet();
         if (widget != null) {
-            widget.updateRequestIds.put(viewId, requestId);
+            widget.updateSequenceNos.put(viewId, requestId);
         }
         if (widget == null || widget.host == null || widget.host.zombie
                 || widget.host.callbacks == null || widget.provider == null
@@ -1941,7 +1944,7 @@
             int appWidgetId, int viewId, long requestId) {
         try {
             callbacks.viewDataChanged(appWidgetId, viewId);
-            host.lastWidgetUpdateRequestId = requestId;
+            host.lastWidgetUpdateSequenceNo = 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.
@@ -1988,9 +1991,9 @@
     }
 
     private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
-        long requestId = REQUEST_COUNTER.incrementAndGet();
+        long requestId = UPDATE_COUNTER.incrementAndGet();
         if (widget != null) {
-            widget.updateRequestIds.put(ID_VIEWS_UPDATE, requestId);
+            widget.updateSequenceNos.put(ID_VIEWS_UPDATE, requestId);
         }
         if (widget == null || widget.provider == null || widget.provider.zombie
                 || widget.host.callbacks == null || widget.host.zombie) {
@@ -2013,7 +2016,7 @@
             int appWidgetId, RemoteViews views, long requestId) {
         try {
             callbacks.updateAppWidget(appWidgetId, views);
-            host.lastWidgetUpdateRequestId = requestId;
+            host.lastWidgetUpdateSequenceNo = requestId;
         } catch (RemoteException re) {
             synchronized (mLock) {
                 Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -2023,11 +2026,11 @@
     }
 
     private void scheduleNotifyProviderChangedLocked(Widget widget) {
-        long requestId = REQUEST_COUNTER.incrementAndGet();
+        long requestId = UPDATE_COUNTER.incrementAndGet();
         if (widget != null) {
             // When the provider changes, reset everything else.
-            widget.updateRequestIds.clear();
-            widget.updateRequestIds.append(ID_PROVIDER_CHANGED, requestId);
+            widget.updateSequenceNos.clear();
+            widget.updateSequenceNos.append(ID_PROVIDER_CHANGED, requestId);
         }
         if (widget == null || widget.provider == null || widget.provider.zombie
                 || widget.host.callbacks == null || widget.host.zombie) {
@@ -2050,7 +2053,7 @@
             int appWidgetId, AppWidgetProviderInfo info, long requestId) {
         try {
             callbacks.providerChanged(appWidgetId, info);
-            host.lastWidgetUpdateRequestId = requestId;
+            host.lastWidgetUpdateSequenceNo = requestId;
         } catch (RemoteException re) {
             synchronized (mLock){
                 Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3887,7 +3890,11 @@
         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 lastWidgetUpdateRequestId; // request id for the last update successfully sent
+        // Sequence no for the last update successfully sent. This is updated whenever a
+        // widget update is successfully sent to the host callbacks. As all new/undelivered updates
+        // will have sequenceNo greater than this, all those updates will be sent when the host
+        // callbacks are attached again.
+        long lastWidgetUpdateSequenceNo;
 
         public int getUserId() {
             return UserHandle.getUserId(id.uid);
@@ -3914,18 +3921,18 @@
          */
         public boolean getPendingUpdatesForId(int appWidgetId,
                 LongSparseArray<PendingHostUpdate> outUpdates) {
-            long updateRequestId = lastWidgetUpdateRequestId;
+            long updateSequenceNo = lastWidgetUpdateSequenceNo;
             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.updateRequestIds.size() - 1; j >= 0; j--) {
-                        long requestId = widget.updateRequestIds.valueAt(j);
-                        if (requestId <= updateRequestId) {
+                    for (int j = widget.updateSequenceNos.size() - 1; j >= 0; j--) {
+                        long requestId = widget.updateSequenceNos.valueAt(j);
+                        if (requestId <= updateSequenceNo) {
                             continue;
                         }
-                        int id = widget.updateRequestIds.keyAt(j);
+                        int id = widget.updateSequenceNos.keyAt(j);
                         final PendingHostUpdate update;
                         switch (id) {
                             case ID_PROVIDER_CHANGED:
@@ -4021,8 +4028,8 @@
         RemoteViews maskedViews;
         Bundle options;
         Host host;
-        // Request ids for various operations
-        SparseLongArray updateRequestIds = new SparseLongArray(2);
+        // Map of request type to updateSequenceNo.
+        SparseLongArray updateSequenceNos = new SparseLongArray(2);
 
         @Override
         public String toString() {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 862070a..880f236 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -216,9 +216,12 @@
                 serviceComponent = ComponentName.unflattenFromString(componentName);
                 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
                         0, mUserId);
+                if (serviceInfo == null) {
+                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
+                }
             } catch (RuntimeException | RemoteException e) {
-                Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
-                return;
+                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
+                serviceInfo = null;
             }
         }
         try {
@@ -228,21 +231,24 @@
                 if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
             } else {
                 mInfo = null;
-                if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
-            }
-            final boolean isEnabled = isEnabled();
-            if (wasEnabled != isEnabled) {
-                if (!isEnabled) {
-                    final int sessionCount = mSessions.size();
-                    for (int i = sessionCount - 1; i >= 0; i--) {
-                        final Session session = mSessions.valueAt(i);
-                        session.removeSelfLocked();
-                    }
+                if (sDebug) {
+                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
                 }
-                sendStateToClients(false);
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
+            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
+            mInfo = null;
+        }
+        final boolean isEnabled = isEnabled();
+        if (wasEnabled != isEnabled) {
+            if (!isEnabled) {
+                final int sessionCount = mSessions.size();
+                for (int i = sessionCount - 1; i >= 0; i--) {
+                    final Session session = mSessions.valueAt(i);
+                    session.removeSelfLocked();
+                }
+            }
+            sendStateToClients(false);
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ed00ffe..a0bbbaf 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -495,7 +495,7 @@
             notifyUnavailableToClient(false);
         }
         synchronized (mLock) {
-            processResponseLocked(response, requestFlags);
+            processResponseLocked(response, null, requestFlags);
         }
 
         final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
@@ -762,13 +762,21 @@
         }
 
         final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
-        if (sDebug) Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result);
+        final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
+        if (sDebug) {
+            Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
+                    + ", clientState=" + newClientState);
+        }
         if (result instanceof FillResponse) {
             writeLog(MetricsEvent.AUTOFILL_AUTHENTICATED);
-            replaceResponseLocked(authenticatedResponse, (FillResponse) result);
+            replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
         } else if (result instanceof Dataset) {
             if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
                 writeLog(MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
+                if (newClientState != null) {
+                    if (sDebug) Slog.d(TAG,  "Updating client state from auth dataset");
+                    mClientState = newClientState;
+                }
                 final Dataset dataset = (Dataset) result;
                 authenticatedResponse.getDatasets().set(datasetIdx, dataset);
                 autoFill(requestId, datasetIdx, dataset, false);
@@ -1543,7 +1551,7 @@
     }
 
     private void replaceResponseLocked(@NonNull FillResponse oldResponse,
-            @NonNull FillResponse newResponse) {
+            @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
         // Disassociate view states with the old response
         setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, true);
         // Move over the id
@@ -1551,7 +1559,7 @@
         // Replace the old response
         mResponses.put(newResponse.getRequestId(), newResponse);
         // Now process the new response
-        processResponseLocked(newResponse, 0);
+        processResponseLocked(newResponse, newClientState, 0);
     }
 
     private void processNullResponseLocked(int flags) {
@@ -1565,7 +1573,8 @@
         removeSelf();
     }
 
-    private void processResponseLocked(@NonNull FillResponse newResponse, int flags) {
+    private void processResponseLocked(@NonNull FillResponse newResponse,
+            @Nullable Bundle newClientState, int flags) {
         // Make sure we are hiding the UI which will be shown
         // only if handling the current response requires it.
         mUi.hideAll(this);
@@ -1573,14 +1582,15 @@
         final int requestId = newResponse.getRequestId();
         if (sVerbose) {
             Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
-                    + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse);
+                    + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse
+                    + ",newClientState=" + newClientState);
         }
 
         if (mResponses == null) {
             mResponses = new SparseArray<>(4);
         }
         mResponses.put(requestId, newResponse);
-        mClientState = newResponse.getClientState();
+        mClientState = newClientState != null ? newClientState : newResponse.getClientState();
 
         setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
         updateTrackedIdsLocked();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index eabe21f..f9213aa 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -319,7 +319,6 @@
     boolean mProvisioned;
     boolean mAutoRestore;
     PowerManager.WakeLock mWakelock;
-    HandlerThread mHandlerThread;
     BackupHandler mBackupHandler;
     PendingIntent mRunBackupIntent, mRunInitIntent;
     BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
@@ -409,43 +408,37 @@
     // Called through the trampoline from onUnlockUser(), then we buck the work
     // off to the background thread to keep the unlock time down.
     public void unlockSystemUser() {
-        mBackupHandler.post(() -> {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-            sInstance.initialize(UserHandle.USER_SYSTEM);
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-            // Migrate legacy setting
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
-            if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+        // Migrate legacy setting
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+        if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+            if (DEBUG) {
+                Slog.i(TAG, "Backup enable apparently not migrated");
+            }
+            final ContentResolver r = sInstance.mContext.getContentResolver();
+            final int enableState = Settings.Secure.getIntForUser(r,
+                    Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+            if (enableState >= 0) {
                 if (DEBUG) {
-                    Slog.i(TAG, "Backup enable apparently not migrated");
+                    Slog.i(TAG, "Migrating enable state " + (enableState != 0));
                 }
-                final ContentResolver r = sInstance.mContext.getContentResolver();
-                final int enableState = Settings.Secure.getIntForUser(r,
-                        Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
-                if (enableState >= 0) {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Migrating enable state " + (enableState != 0));
-                    }
-                    writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
-                    Settings.Secure.putStringForUser(r,
-                            Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
-                } else {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup not yet configured; retaining null enable state");
-                    }
+                writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+                Settings.Secure.putStringForUser(r,
+                        Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+            } else {
+                if (DEBUG) {
+                    Slog.i(TAG, "Backup not yet configured; retaining null enable state");
                 }
             }
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-            try {
-                sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
-            } catch (RemoteException e) {
-                // can't happen; it's a local object
-            }
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        });
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+        try {
+            sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+        } catch (RemoteException e) {
+            // can't happen; it's a local object
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     class ProvisionedObserver extends ContentObserver {
@@ -1220,7 +1213,7 @@
 
     // ----- Main service implementation -----
 
-    public BackupManagerService(Context context, Trampoline parent) {
+    public BackupManagerService(Context context, Trampoline parent, HandlerThread backupThread) {
         mContext = context;
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -1233,9 +1226,7 @@
         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
 
         // spin up the backup/restore handler thread
-        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
-        mHandlerThread.start();
-        mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
+        mBackupHandler = new BackupHandler(backupThread.getLooper());
 
         // Set up our bookkeeping
         final ContentResolver resolver = context.getContentResolver();
@@ -1360,7 +1351,7 @@
         if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
 
         mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
-                mTransportBoundListener, mHandlerThread.getLooper());
+                mTransportBoundListener, backupThread.getLooper());
         mTransportManager.registerAllTransports();
 
         // Now that we know about valid backup participants, parse any
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index f298065..20f2369 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -237,7 +237,6 @@
     private boolean mProvisioned;
     private boolean mAutoRestore;
     private PowerManager.WakeLock mWakelock;
-    private HandlerThread mHandlerThread;
     private BackupHandler mBackupHandler;
     private PendingIntent mRunBackupIntent;
     private PendingIntent mRunInitIntent;
@@ -556,43 +555,37 @@
     // Called through the trampoline from onUnlockUser(), then we buck the work
     // off to the background thread to keep the unlock time down.
     public void unlockSystemUser() {
-        mBackupHandler.post(() -> {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-            sInstance.initialize(UserHandle.USER_SYSTEM);
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-            // Migrate legacy setting
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
-            if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+        // Migrate legacy setting
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+        if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+            if (DEBUG) {
+                Slog.i(TAG, "Backup enable apparently not migrated");
+            }
+            final ContentResolver r = sInstance.mContext.getContentResolver();
+            final int enableState = Settings.Secure.getIntForUser(r,
+                    Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+            if (enableState >= 0) {
                 if (DEBUG) {
-                    Slog.i(TAG, "Backup enable apparently not migrated");
+                    Slog.i(TAG, "Migrating enable state " + (enableState != 0));
                 }
-                final ContentResolver r = sInstance.mContext.getContentResolver();
-                final int enableState = Settings.Secure.getIntForUser(r,
-                        Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
-                if (enableState >= 0) {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Migrating enable state " + (enableState != 0));
-                    }
-                    writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
-                    Settings.Secure.putStringForUser(r,
-                            Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
-                } else {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup not yet configured; retaining null enable state");
-                    }
+                writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+                Settings.Secure.putStringForUser(r,
+                        Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+            } else {
+                if (DEBUG) {
+                    Slog.i(TAG, "Backup not yet configured; retaining null enable state");
                 }
             }
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-            try {
-                sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
-            } catch (RemoteException e) {
-                // can't happen; it's a local object
-            }
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        });
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+        try {
+            sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+        } catch (RemoteException e) {
+            // can't happen; it's a local object
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
@@ -729,7 +722,8 @@
 
     // ----- Main service implementation -----
 
-    public RefactoredBackupManagerService(Context context, Trampoline parent) {
+    public RefactoredBackupManagerService(Context context, Trampoline parent,
+            HandlerThread backupThread) {
         mContext = context;
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -742,9 +736,7 @@
         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
 
         // spin up the backup/restore handler thread
-        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
-        mHandlerThread.start();
-        mBackupHandler = new BackupHandler(this, mHandlerThread.getLooper());
+        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
 
         // Set up our bookkeeping
         final ContentResolver resolver = context.getContentResolver();
@@ -824,7 +816,7 @@
         if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
 
         mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
-                mTransportBoundListener, mHandlerThread.getLooper());
+                mTransportBoundListener, backupThread.getLooper());
         mTransportManager.registerAllTransports();
 
         // Now that we know about valid backup participants, parse any
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 9739e38..9847edf 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -28,11 +28,15 @@
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
@@ -75,6 +79,8 @@
     final boolean mGlobalDisable;
     volatile BackupManagerServiceInterface mService;
 
+    private HandlerThread mHandlerThread;
+
     public Trampoline(Context context) {
         mContext = context;
         mGlobalDisable = isBackupDisabled();
@@ -111,11 +117,11 @@
     }
 
     protected BackupManagerServiceInterface createRefactoredBackupManagerService() {
-        return new RefactoredBackupManagerService(mContext, this);
+        return new RefactoredBackupManagerService(mContext, this, mHandlerThread);
     }
 
     protected BackupManagerServiceInterface createBackupManagerService() {
-        return new BackupManagerService(mContext, this);
+        return new BackupManagerService(mContext, this, mHandlerThread);
     }
 
     // internal control API
@@ -140,10 +146,21 @@
     }
 
     void unlockSystemUser() {
-        BackupManagerServiceInterface svc = mService;
-        if (svc != null) {
-            svc.unlockSystemUser();
-        }
+        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
+        mHandlerThread.start();
+
+        Handler h = new Handler(mHandlerThread.getLooper());
+        h.post(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+            initialize(UserHandle.USER_SYSTEM);
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+            BackupManagerServiceInterface svc = mService;
+            Slog.i(TAG, "Unlocking system user; mService=" + mService);
+            if (svc != null) {
+                svc.unlockSystemUser();
+            }
+        });
     }
 
     public void setBackupServiceActive(final int userHandle, boolean makeActive) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 5106c8d..6d9c977 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -35,6 +35,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.hardware.health.V2_0.HealthInfo;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
 import android.os.BatteryProperties;
@@ -118,8 +119,8 @@
 
     private final Object mLock = new Object();
 
-    private BatteryProperties mBatteryProps;
-    private final BatteryProperties mLastBatteryProps = new BatteryProperties();
+    private HealthInfo mHealthInfo;
+    private final HealthInfo mLastHealthInfo = new HealthInfo();
     private boolean mBatteryLevelCritical;
     private int mLastBatteryStatus;
     private int mLastBatteryHealth;
@@ -251,16 +252,16 @@
     private boolean isPoweredLocked(int plugTypeSet) {
         // assume we are powered if battery state is unknown so
         // the "stay on while plugged in" option will work.
-        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
+        if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
             return true;
         }
-        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
+        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.legacy.chargerAcOnline) {
             return true;
         }
-        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
+        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.legacy.chargerUsbOnline) {
             return true;
         }
-        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
+        if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.legacy.chargerWirelessOnline) {
             return true;
         }
         return false;
@@ -277,15 +278,15 @@
          *   (becomes <= mLowBatteryWarningLevel).
          */
         return !plugged
-                && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
-                && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
+                && mHealthInfo.legacy.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel
                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
     }
 
     private void shutdownIfNoPowerLocked() {
         // shut down gracefully if our battery is critically low and we are not powered.
         // wait until the system has booted before attempting to display the shutdown dialog.
-        if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+        if (mHealthInfo.legacy.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -306,7 +307,7 @@
         // shut down gracefully if temperature is too high (> 68.0C by default)
         // wait until the system has booted before attempting to display the
         // shutdown dialog.
-        if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
+        if (mHealthInfo.legacy.batteryTemperature > mShutdownBatteryTemperature) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -326,25 +327,66 @@
     private void update(BatteryProperties props) {
         synchronized (mLock) {
             if (!mUpdatesStopped) {
-                mBatteryProps = props;
+                mHealthInfo = new HealthInfo();
+                copy(mHealthInfo, props);
                 // Process the new values.
                 processValuesLocked(false);
             } else {
-                mLastBatteryProps.set(props);
+                copy(mLastHealthInfo, props);
             }
         }
     }
 
+    private static void copy(HealthInfo dst, HealthInfo src) {
+        dst.legacy.chargerAcOnline = src.legacy.chargerAcOnline;
+        dst.legacy.chargerUsbOnline = src.legacy.chargerUsbOnline;
+        dst.legacy.chargerWirelessOnline = src.legacy.chargerWirelessOnline;
+        dst.legacy.maxChargingCurrent = src.legacy.maxChargingCurrent;
+        dst.legacy.maxChargingVoltage = src.legacy.maxChargingVoltage;
+        dst.legacy.batteryStatus = src.legacy.batteryStatus;
+        dst.legacy.batteryHealth = src.legacy.batteryHealth;
+        dst.legacy.batteryPresent = src.legacy.batteryPresent;
+        dst.legacy.batteryLevel = src.legacy.batteryLevel;
+        dst.legacy.batteryVoltage = src.legacy.batteryVoltage;
+        dst.legacy.batteryTemperature = src.legacy.batteryTemperature;
+        dst.legacy.batteryCurrent = src.legacy.batteryCurrent;
+        dst.legacy.batteryCycleCount = src.legacy.batteryCycleCount;
+        dst.legacy.batteryFullCharge = src.legacy.batteryFullCharge;
+        dst.legacy.batteryChargeCounter = src.legacy.batteryChargeCounter;
+        dst.legacy.batteryTechnology = src.legacy.batteryTechnology;
+        dst.batteryCurrentAverage = src.batteryCurrentAverage;
+        dst.batteryCapacity = src.batteryCapacity;
+        dst.energyCounter = src.energyCounter;
+    }
+
+    // TODO(b/62229583): remove this function when BatteryProperties are completely replaced.
+    private static void copy(HealthInfo dst, BatteryProperties src) {
+        dst.legacy.chargerAcOnline = src.chargerAcOnline;
+        dst.legacy.chargerUsbOnline = src.chargerUsbOnline;
+        dst.legacy.chargerWirelessOnline = src.chargerWirelessOnline;
+        dst.legacy.maxChargingCurrent = src.maxChargingCurrent;
+        dst.legacy.maxChargingVoltage = src.maxChargingVoltage;
+        dst.legacy.batteryStatus = src.batteryStatus;
+        dst.legacy.batteryHealth = src.batteryHealth;
+        dst.legacy.batteryPresent = src.batteryPresent;
+        dst.legacy.batteryLevel = src.batteryLevel;
+        dst.legacy.batteryVoltage = src.batteryVoltage;
+        dst.legacy.batteryTemperature = src.batteryTemperature;
+        dst.legacy.batteryFullCharge = src.batteryFullCharge;
+        dst.legacy.batteryChargeCounter = src.batteryChargeCounter;
+        dst.legacy.batteryTechnology = src.batteryTechnology;
+    }
+
     private void processValuesLocked(boolean force) {
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
-        if (mBatteryProps.chargerAcOnline) {
+        mBatteryLevelCritical = (mHealthInfo.legacy.batteryLevel <= mCriticalBatteryLevel);
+        if (mHealthInfo.legacy.chargerAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
-        } else if (mBatteryProps.chargerUsbOnline) {
+        } else if (mHealthInfo.legacy.chargerUsbOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
-        } else if (mBatteryProps.chargerWirelessOnline) {
+        } else if (mHealthInfo.legacy.chargerWirelessOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
         } else {
             mPlugType = BATTERY_PLUGGED_NONE;
@@ -352,30 +394,17 @@
 
         if (DEBUG) {
             Slog.d(TAG, "Processing new values: "
-                    + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
-                    + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
-                    + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
-                    + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
-                    + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
-                    + ", batteryStatus=" + mBatteryProps.batteryStatus
-                    + ", batteryHealth=" + mBatteryProps.batteryHealth
-                    + ", batteryPresent=" + mBatteryProps.batteryPresent
-                    + ", batteryLevel=" + mBatteryProps.batteryLevel
-                    + ", batteryTechnology=" + mBatteryProps.batteryTechnology
-                    + ", batteryVoltage=" + mBatteryProps.batteryVoltage
-                    + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
-                    + ", batteryFullCharge=" + mBatteryProps.batteryFullCharge
-                    + ", batteryTemperature=" + mBatteryProps.batteryTemperature
+                    + "info=" + mHealthInfo
                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                     + ", mPlugType=" + mPlugType);
         }
 
         // Let the battery stats keep track of the current level.
         try {
-            mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
-                    mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
-                    mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter,
-                    mBatteryProps.batteryFullCharge);
+            mBatteryStats.setBatteryState(mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth,
+                    mPlugType, mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryTemperature,
+                    mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryChargeCounter,
+                    mHealthInfo.legacy.batteryFullCharge);
         } catch (RemoteException e) {
             // Should never happen.
         }
@@ -383,16 +412,16 @@
         shutdownIfNoPowerLocked();
         shutdownIfOverTempLocked();
 
-        if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
-                mBatteryProps.batteryHealth != mLastBatteryHealth ||
-                mBatteryProps.batteryPresent != mLastBatteryPresent ||
-                mBatteryProps.batteryLevel != mLastBatteryLevel ||
+        if (force || (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
+                mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
+                mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
+                mHealthInfo.legacy.batteryLevel != mLastBatteryLevel ||
                 mPlugType != mLastPlugType ||
-                mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
-                mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
-                mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
-                mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
-                mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
+                mHealthInfo.legacy.batteryVoltage != mLastBatteryVoltage ||
+                mHealthInfo.legacy.batteryTemperature != mLastBatteryTemperature ||
+                mHealthInfo.legacy.maxChargingCurrent != mLastMaxChargingCurrent ||
+                mHealthInfo.legacy.maxChargingVoltage != mLastMaxChargingVoltage ||
+                mHealthInfo.legacy.batteryChargeCounter != mLastChargeCounter ||
                 mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
@@ -401,33 +430,33 @@
 
                     // There's no value in this data unless we've discharged at least once and the
                     // battery level has changed; so don't log until it does.
-                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
+                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.legacy.batteryLevel) {
                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                         logOutlier = true;
                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
-                                mDischargeStartLevel, mBatteryProps.batteryLevel);
+                                mDischargeStartLevel, mHealthInfo.legacy.batteryLevel);
                         // make sure we see a discharge event before logging again
                         mDischargeStartTime = 0;
                     }
                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
                     // charging -> discharging or we just powered up
                     mDischargeStartTime = SystemClock.elapsedRealtime();
-                    mDischargeStartLevel = mBatteryProps.batteryLevel;
+                    mDischargeStartLevel = mHealthInfo.legacy.batteryLevel;
                 }
             }
-            if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
-                    mBatteryProps.batteryHealth != mLastBatteryHealth ||
-                    mBatteryProps.batteryPresent != mLastBatteryPresent ||
+            if (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
+                    mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
+                    mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
                     mPlugType != mLastPlugType) {
                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
-                        mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
-                        mPlugType, mBatteryProps.batteryTechnology);
+                        mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth, mHealthInfo.legacy.batteryPresent ? 1 : 0,
+                        mPlugType, mHealthInfo.legacy.batteryTechnology);
             }
-            if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
+            if (mHealthInfo.legacy.batteryLevel != mLastBatteryLevel) {
                 // Don't do this just from voltage or temperature changes, that is
                 // too noisy.
                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
-                        mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
+                        mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryTemperature);
             }
             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                     mPlugType == BATTERY_PLUGGED_NONE) {
@@ -440,16 +469,16 @@
             if (!mBatteryLevelLow) {
                 // Should we now switch in to low battery mode?
                 if (mPlugType == BATTERY_PLUGGED_NONE
-                        && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
+                        && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel) {
                     mBatteryLevelLow = true;
                 }
             } else {
                 // Should we now switch out of low battery mode?
                 if (mPlugType != BATTERY_PLUGGED_NONE) {
                     mBatteryLevelLow = false;
-                } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel)  {
+                } else if (mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel)  {
                     mBatteryLevelLow = false;
-                } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
+                } else if (force && mHealthInfo.legacy.batteryLevel >= mLowBatteryWarningLevel) {
                     // If being forced, the previous state doesn't matter, we will just
                     // absolutely check to see if we are now above the warning level.
                     mBatteryLevelLow = false;
@@ -496,7 +525,7 @@
                     }
                 });
             } else if (mSentLowBatteryBroadcast &&
-                    mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
+                    mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
                 mSentLowBatteryBroadcast = false;
                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -522,16 +551,16 @@
                 logOutlierLocked(dischargeDuration);
             }
 
-            mLastBatteryStatus = mBatteryProps.batteryStatus;
-            mLastBatteryHealth = mBatteryProps.batteryHealth;
-            mLastBatteryPresent = mBatteryProps.batteryPresent;
-            mLastBatteryLevel = mBatteryProps.batteryLevel;
+            mLastBatteryStatus = mHealthInfo.legacy.batteryStatus;
+            mLastBatteryHealth = mHealthInfo.legacy.batteryHealth;
+            mLastBatteryPresent = mHealthInfo.legacy.batteryPresent;
+            mLastBatteryLevel = mHealthInfo.legacy.batteryLevel;
             mLastPlugType = mPlugType;
-            mLastBatteryVoltage = mBatteryProps.batteryVoltage;
-            mLastBatteryTemperature = mBatteryProps.batteryTemperature;
-            mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
-            mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
-            mLastChargeCounter = mBatteryProps.batteryChargeCounter;
+            mLastBatteryVoltage = mHealthInfo.legacy.batteryVoltage;
+            mLastBatteryTemperature = mHealthInfo.legacy.batteryTemperature;
+            mLastMaxChargingCurrent = mHealthInfo.legacy.maxChargingCurrent;
+            mLastMaxChargingVoltage = mHealthInfo.legacy.maxChargingVoltage;
+            mLastChargeCounter = mHealthInfo.legacy.batteryChargeCounter;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
         }
@@ -543,38 +572,26 @@
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
 
-        int icon = getIconLocked(mBatteryProps.batteryLevel);
+        int icon = getIconLocked(mHealthInfo.legacy.batteryLevel);
 
         intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
-        intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
-        intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
-        intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
-        intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
+        intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.legacy.batteryStatus);
+        intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.legacy.batteryHealth);
+        intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.legacy.batteryPresent);
+        intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.legacy.batteryLevel);
         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
-        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
-        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
-        intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
+        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.legacy.batteryVoltage);
+        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
+        intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
-        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
-        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
-        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
+        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
+        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
+        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
         if (DEBUG) {
-            Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +
-                    ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
-                    ", health:" + mBatteryProps.batteryHealth +
-                    ", present:" + mBatteryProps.batteryPresent +
-                    ", voltage: " + mBatteryProps.batteryVoltage +
-                    ", temperature: " + mBatteryProps.batteryTemperature +
-                    ", technology: " + mBatteryProps.batteryTechnology +
-                    ", AC powered:" + mBatteryProps.chargerAcOnline +
-                    ", USB powered:" + mBatteryProps.chargerUsbOnline +
-                    ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
-                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger +
-                    ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
-                    ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage +
-                    ", chargeCounter:" + mBatteryProps.batteryChargeCounter);
+            Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+                    + ", info:" + mHealthInfo.toString());
         }
 
         mHandler.post(new Runnable() {
@@ -635,14 +652,14 @@
                 long durationThreshold = Long.parseLong(durationThresholdString);
                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
                 if (duration <= durationThreshold &&
-                        mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {
+                        mDischargeStartLevel - mHealthInfo.legacy.batteryLevel >= dischargeThreshold) {
                     // If the discharge cycle is bad enough we want to know about it.
                     logBatteryStatsLocked();
                 }
                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
                         " discharge threshold: " + dischargeThreshold);
                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
-                        (mDischargeStartLevel - mBatteryProps.batteryLevel));
+                        (mDischargeStartLevel - mHealthInfo.legacy.batteryLevel));
             } catch (NumberFormatException e) {
                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
                         durationThresholdString + " or " + dischargeThresholdString);
@@ -651,14 +668,14 @@
     }
 
     private int getIconLocked(int level) {
-        if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
+        if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
             return com.android.internal.R.drawable.stat_sys_battery_charge;
-        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+        } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
             return com.android.internal.R.drawable.stat_sys_battery;
-        } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
-                || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+        } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+                || mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
-                    && mBatteryProps.batteryLevel >= 100) {
+                    && mHealthInfo.legacy.batteryLevel >= 100) {
                 return com.android.internal.R.drawable.stat_sys_battery_charge;
             } else {
                 return com.android.internal.R.drawable.stat_sys_battery;
@@ -720,11 +737,11 @@
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
                 if (!mUpdatesStopped) {
-                    mLastBatteryProps.set(mBatteryProps);
+                    copy(mLastHealthInfo, mHealthInfo);
                 }
-                mBatteryProps.chargerAcOnline = false;
-                mBatteryProps.chargerUsbOnline = false;
-                mBatteryProps.chargerWirelessOnline = false;
+                mHealthInfo.legacy.chargerAcOnline = false;
+                mHealthInfo.legacy.chargerUsbOnline = false;
+                mHealthInfo.legacy.chargerWirelessOnline = false;
                 long ident = Binder.clearCallingIdentity();
                 try {
                     mUpdatesStopped = true;
@@ -751,30 +768,30 @@
                 }
                 try {
                     if (!mUpdatesStopped) {
-                        mLastBatteryProps.set(mBatteryProps);
+                        copy(mLastHealthInfo, mHealthInfo);
                     }
                     boolean update = true;
                     switch (key) {
                         case "present":
-                            mBatteryProps.batteryPresent = Integer.parseInt(value) != 0;
+                            mHealthInfo.legacy.batteryPresent = Integer.parseInt(value) != 0;
                             break;
                         case "ac":
-                            mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+                            mHealthInfo.legacy.chargerAcOnline = Integer.parseInt(value) != 0;
                             break;
                         case "usb":
-                            mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
+                            mHealthInfo.legacy.chargerUsbOnline = Integer.parseInt(value) != 0;
                             break;
                         case "wireless":
-                            mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
+                            mHealthInfo.legacy.chargerWirelessOnline = Integer.parseInt(value) != 0;
                             break;
                         case "status":
-                            mBatteryProps.batteryStatus = Integer.parseInt(value);
+                            mHealthInfo.legacy.batteryStatus = Integer.parseInt(value);
                             break;
                         case "level":
-                            mBatteryProps.batteryLevel = Integer.parseInt(value);
+                            mHealthInfo.legacy.batteryLevel = Integer.parseInt(value);
                             break;
                         case "temp":
-                            mBatteryProps.batteryTemperature = Integer.parseInt(value);
+                            mHealthInfo.legacy.batteryTemperature = Integer.parseInt(value);
                             break;
                         case "invalid":
                             mInvalidCharger = Integer.parseInt(value);
@@ -806,7 +823,7 @@
                 try {
                     if (mUpdatesStopped) {
                         mUpdatesStopped = false;
-                        mBatteryProps.set(mLastBatteryProps);
+                        copy(mHealthInfo, mLastHealthInfo);
                         processValuesFromShellLocked(pw, opts);
                     }
                 } finally {
@@ -833,20 +850,20 @@
                 if (mUpdatesStopped) {
                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
                 }
-                pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);
-                pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);
-                pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);
-                pw.println("  Max charging current: " + mBatteryProps.maxChargingCurrent);
-                pw.println("  Max charging voltage: " + mBatteryProps.maxChargingVoltage);
-                pw.println("  Charge counter: " + mBatteryProps.batteryChargeCounter);
-                pw.println("  status: " + mBatteryProps.batteryStatus);
-                pw.println("  health: " + mBatteryProps.batteryHealth);
-                pw.println("  present: " + mBatteryProps.batteryPresent);
-                pw.println("  level: " + mBatteryProps.batteryLevel);
+                pw.println("  AC powered: " + mHealthInfo.legacy.chargerAcOnline);
+                pw.println("  USB powered: " + mHealthInfo.legacy.chargerUsbOnline);
+                pw.println("  Wireless powered: " + mHealthInfo.legacy.chargerWirelessOnline);
+                pw.println("  Max charging current: " + mHealthInfo.legacy.maxChargingCurrent);
+                pw.println("  Max charging voltage: " + mHealthInfo.legacy.maxChargingVoltage);
+                pw.println("  Charge counter: " + mHealthInfo.legacy.batteryChargeCounter);
+                pw.println("  status: " + mHealthInfo.legacy.batteryStatus);
+                pw.println("  health: " + mHealthInfo.legacy.batteryHealth);
+                pw.println("  present: " + mHealthInfo.legacy.batteryPresent);
+                pw.println("  level: " + mHealthInfo.legacy.batteryLevel);
                 pw.println("  scale: " + BATTERY_SCALE);
-                pw.println("  voltage: " + mBatteryProps.batteryVoltage);
-                pw.println("  temperature: " + mBatteryProps.batteryTemperature);
-                pw.println("  technology: " + mBatteryProps.batteryTechnology);
+                pw.println("  voltage: " + mHealthInfo.legacy.batteryVoltage);
+                pw.println("  temperature: " + mHealthInfo.legacy.batteryTemperature);
+                pw.println("  technology: " + mHealthInfo.legacy.batteryTechnology);
             } else {
                 Shell shell = new Shell();
                 shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
@@ -860,25 +877,25 @@
         synchronized (mLock) {
             proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
             int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE;
-            if (mBatteryProps.chargerAcOnline) {
+            if (mHealthInfo.legacy.chargerAcOnline) {
                 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC;
-            } else if (mBatteryProps.chargerUsbOnline) {
+            } else if (mHealthInfo.legacy.chargerUsbOnline) {
                 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB;
-            } else if (mBatteryProps.chargerWirelessOnline) {
+            } else if (mHealthInfo.legacy.chargerWirelessOnline) {
                 batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS;
             }
             proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
-            proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
-            proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
-            proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
-            proto.write(BatteryServiceDumpProto.STATUS, mBatteryProps.batteryStatus);
-            proto.write(BatteryServiceDumpProto.HEALTH, mBatteryProps.batteryHealth);
-            proto.write(BatteryServiceDumpProto.IS_PRESENT, mBatteryProps.batteryPresent);
-            proto.write(BatteryServiceDumpProto.LEVEL, mBatteryProps.batteryLevel);
+            proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
+            proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
+            proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
+            proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.legacy.batteryStatus);
+            proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.legacy.batteryHealth);
+            proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.legacy.batteryPresent);
+            proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.legacy.batteryLevel);
             proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
-            proto.write(BatteryServiceDumpProto.VOLTAGE, mBatteryProps.batteryVoltage);
-            proto.write(BatteryServiceDumpProto.TEMPERATURE, mBatteryProps.batteryTemperature);
-            proto.write(BatteryServiceDumpProto.TECHNOLOGY, mBatteryProps.batteryTechnology);
+            proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.legacy.batteryVoltage);
+            proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
+            proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
         }
         proto.flush();
     }
@@ -911,8 +928,8 @@
          * Synchronize on BatteryService.
          */
         public void updateLightsLocked() {
-            final int level = mBatteryProps.batteryLevel;
-            final int status = mBatteryProps.batteryStatus;
+            final int level = mHealthInfo.legacy.batteryLevel;
+            final int status = mHealthInfo.legacy.batteryStatus;
             if (level < mLowBatteryWarningLevel) {
                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                     // Solid red when battery is charging
@@ -985,7 +1002,7 @@
         @Override
         public int getBatteryLevel() {
             synchronized (mLock) {
-                return mBatteryProps.batteryLevel;
+                return mHealthInfo.legacy.batteryLevel;
             }
         }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 046eb76..8b79b9d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -373,12 +373,24 @@
             if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
                     && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
                 if (DEBUG) {
-                    Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
+                    Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
                 }
                 return;
             }
         }
 
+        // If the current vibration is repeating and the incoming one is non-repeating, then ignore
+        // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
+        // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
+        // vibrations that are likely quite short.
+        if (!isRepeatingVibration(effect)
+                && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
+            if (DEBUG) {
+                Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+            }
+            return;
+        }
+
         Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
 
         // Only link against waveforms since they potentially don't have a finish if
@@ -404,6 +416,16 @@
         }
     }
 
+    private static boolean isRepeatingVibration(VibrationEffect effect) {
+        if (effect instanceof VibrationEffect.Waveform) {
+            final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
+            if (waveform.getRepeatIndex() >= 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void addToPreviousVibrationsLocked(Vibration vib) {
         if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
             mPreviousVibrations.removeFirst();
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 8bcbfbe..8839cfc 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,9 +16,6 @@
 
 package com.android.server.am;
 
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -213,7 +210,11 @@
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
             // TODO: Should be okay to have stacks with with undefined windowing mode long term, but
             // have to set them to something for now due to logic that depending on them.
-            windowingMode = WINDOWING_MODE_FULLSCREEN;
+            windowingMode = getWindowingMode(); // Put in current display's windowing mode
+            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+                // Else fullscreen for now...
+                windowingMode = WINDOWING_MODE_FULLSCREEN;
+            }
         }
 
         final boolean inSplitScreenMode = hasSplitScreenStack();
@@ -228,24 +229,7 @@
             windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
         }
 
-        int stackId = INVALID_STACK_ID;
-        if (mDisplayId == DEFAULT_DISPLAY && (activityType == ACTIVITY_TYPE_STANDARD
-                || activityType == ACTIVITY_TYPE_UNDEFINED)) {
-            // TODO: Will be removed once we are no longer using static stack ids.
-            stackId = getStackIdForWindowingMode(windowingMode);
-            if (stackId == INVALID_STACK_ID) {
-                // Whatever...put in fullscreen stack for now.
-                stackId = FULLSCREEN_WORKSPACE_STACK_ID;
-            }
-            final T stack = getStack(stackId);
-            if (stack != null) {
-                return stack;
-            }
-        }
-
-        if (stackId == INVALID_STACK_ID) {
-            stackId = mSupervisor.getNextStackId();
-        }
+        final int stackId = mSupervisor.getNextStackId();
 
         final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 3a9bf12..ceb2ad6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -59,7 +59,6 @@
     static final boolean DEBUG_FOCUS = false;
     static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKSCREEN = DEBUG_ALL || false;
     static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
@@ -74,10 +73,10 @@
     static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
     static final boolean DEBUG_PSS = DEBUG_ALL || false;
     static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
+    static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
     static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
     static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
@@ -85,7 +84,6 @@
     static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
     static final boolean DEBUG_TASKS = DEBUG_ALL || false;
-    static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
     static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
     static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
     static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
@@ -105,7 +103,6 @@
     static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : "";
     static final String POSTFIX_IDLE = (APPEND_CATEGORY_NAME) ? "_Idle" : "";
     static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : "";
-    static final String POSTFIX_LOCKSCREEN = (APPEND_CATEGORY_NAME) ? "_LockScreen" : "";
     static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : "";
     static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
     static final String POSTFIX_MU = "_MU";
@@ -122,7 +119,6 @@
     static final String POSTFIX_RELEASE = (APPEND_CATEGORY_NAME) ? "_Release" : "";
     static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : "";
     static final String POSTFIX_SAVED_STATE = (APPEND_CATEGORY_NAME) ? "_SavedState" : "";
-    static final String POSTFIX_SCREENSHOTS = (APPEND_CATEGORY_NAME) ? "_Screenshots" : "";
     static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
     static final String POSTFIX_SERVICE_EXECUTING =
             (APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
@@ -130,13 +126,11 @@
     static final String POSTFIX_STATES = (APPEND_CATEGORY_NAME) ? "_States" : "";
     static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : "";
     static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
-    static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
     static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
     static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
             ? "_UidObservers" : "";
     static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
     static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
     static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
-    static final String POSTFIX_VISIBLE_BEHIND = (APPEND_CATEGORY_NAME) ? "_VisibleBehind" : "";
 
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e6fe620..3955339 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,18 +27,10 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
-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.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
-import static android.app.ActivityManager.StackId.isStaticStack;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -57,6 +49,9 @@
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.os.Build.VERSION_CODES.N;
+import static android.os.IServiceManager.DUMP_PRIORITY_CRITICAL;
+import static android.os.IServiceManager.DUMP_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_PRIORITY_NORMAL;
 import static android.os.Process.BLUETOOTH_UID;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.FIRST_ISOLATED_UID;
@@ -137,7 +132,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
@@ -199,7 +193,6 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManagerInternal;
@@ -739,9 +732,6 @@
                 doDump(fd, pw, new String[] {"associations"});
             }
             doDump(fd, pw, new String[] {"processes"});
-            doDump(fd, pw, new String[] {"-v", "all"});
-            doDump(fd, pw, new String[] {"service", "all"});
-            doDump(fd, pw, new String[] {"provider", "all"});
         }
 
         @Override
@@ -753,6 +743,8 @@
     public boolean canShowErrorDialogs() {
         return mShowDialogs && !mSleeping && !mShuttingDown
                 && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
+                && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
+                        mUserController.getCurrentUserId())
                 && !(UserManager.isDeviceInDemoMode(mContext)
                         && mUserController.getCurrentUser().isDemo());
     }
@@ -1738,9 +1730,6 @@
      */
     private boolean mUserIsMonkey;
 
-    /** Flag whether the device has a Recents UI */
-    boolean mHasRecents;
-
     /** The dimensions of the thumbnails in the Recents UI. */
     int mThumbnailWidth;
     int mThumbnailHeight;
@@ -2111,7 +2100,8 @@
                     String text = mContext.getString(R.string.heavy_weight_notification,
                             context.getApplicationInfo().loadLabel(context.getPackageManager()));
                     Notification notification =
-                            new Notification.Builder(context, SystemNotificationChannels.DEVELOPER)
+                            new Notification.Builder(context,
+                                    SystemNotificationChannels.HEAVY_WEIGHT_APP)
                             .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
                             .setWhen(0)
                             .setOngoing(true)
@@ -2145,7 +2135,7 @@
                 }
                 try {
                     inm.cancelNotificationWithTag("android", null,
-                            SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION,  msg.arg1);
+                            SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
                 } catch (RuntimeException e) {
                     Slog.w(ActivityManagerService.TAG,
                             "Error canceling notification for service", e);
@@ -2503,13 +2493,16 @@
 
     public void setSystemProcess() {
         try {
-            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
+            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
+                    DUMP_PRIORITY_CRITICAL | DUMP_PRIORITY_NORMAL);
             ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
-            ServiceManager.addService("meminfo", new MemBinder(this));
+            ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
+                    DUMP_PRIORITY_HIGH | DUMP_PRIORITY_NORMAL);
             ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
             ServiceManager.addService("dbinfo", new DbBinder(this));
             if (MONITOR_CPU_USAGE) {
-                ServiceManager.addService("cpuinfo", new CpuBinder(this));
+                ServiceManager.addService("cpuinfo", new CpuBinder(this),
+                        /* allowIsolated= */ false, DUMP_PRIORITY_CRITICAL);
             }
             ServiceManager.addService("permission", new PermissionController(this));
             ServiceManager.addService("processinfo", new ProcessInfoService(this));
@@ -2784,6 +2777,7 @@
                 new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
         mActivityStarter = new ActivityStarter(this, mStackSupervisor);
         mRecentTasks = new RecentTasks(this, mStackSupervisor);
+        mStackSupervisor.setRecentTasks(mRecentTasks);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
 
         mProcessCpuThread = new Thread("CpuTracker") {
@@ -3241,11 +3235,12 @@
         // stack implementation changes in the future, keep in mind that the use of the fullscreen
         // stack is a means to move the activity to the main display and a moveActivityToDisplay()
         // option would be a better choice here.
-        if (r.requestedVrComponent != null && r.getStackId() >= FIRST_DYNAMIC_STACK_ID) {
+        if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
             Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
                     + " to main stack for VR");
-            setTaskWindowingMode(r.getTask().taskId,
-                    WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
+            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+                    WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
+            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
         }
         mHandler.sendMessage(
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
@@ -5095,11 +5090,12 @@
         }
 
         synchronized(this) {
-            if (mHeavyWeightProcess == null) {
+            final ProcessRecord proc = mHeavyWeightProcess;
+            if (proc == null) {
                 return;
             }
 
-            ArrayList<ActivityRecord> activities = new ArrayList<>(mHeavyWeightProcess.activities);
+            ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
             for (int i = 0; i < activities.size(); i++) {
                 ActivityRecord r = activities.get(i);
                 if (!r.finishing && r.isInStackLocked()) {
@@ -5109,7 +5105,7 @@
             }
 
             mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
-                    mHeavyWeightProcess.userId, 0));
+                    proc.userId, 0));
             mHeavyWeightProcess = null;
         }
     }
@@ -5963,16 +5959,7 @@
 
                 if (appInfo != null) {
                     forceStopPackageLocked(packageName, appInfo.uid, "clear data");
-                    // Remove all tasks match the cleared application package and user
-                    for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
-                        final TaskRecord tr = mRecentTasks.get(i);
-                        final String taskPackageName =
-                                tr.getBaseIntent().getComponent().getPackageName();
-                        if (tr.userId != resolvedUserId) continue;
-                        if (!taskPackageName.equals(packageName)) continue;
-                        mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
-                                REMOVE_FROM_RECENTS);
-                    }
+                    mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId);
                 }
             }
 
@@ -6553,7 +6540,7 @@
         }
 
         // Clean-up disabled tasks
-        cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+        mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
 
         // Clean-up disabled services.
         mServices.bringDownDisabledPackageServicesLocked(
@@ -8084,8 +8071,8 @@
     }
 
     private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getStack() == null || !r.getStack().isPinnedStack() ||
-                r.getStack().isInStackLocked(r) == null) {
+        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
+                || r.getStack().isInStackLocked(r) == null) {
             return false;
         }
 
@@ -8179,7 +8166,7 @@
 
                 // Only update the saved args from the args that are set
                 r.pictureInPictureArgs.copyOnlySet(params);
-                if (r.getStack().getStackId() == PINNED_STACK_ID) {
+                if (r.inPinnedWindowingMode()) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
@@ -9800,35 +9787,12 @@
     public List<IBinder> getAppTasks(String callingPackage) {
         int callingUid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
-
-        synchronized(this) {
-            ArrayList<IBinder> list = new ArrayList<IBinder>();
-            try {
-                if (DEBUG_ALL) Slog.v(TAG, "getAppTasks");
-
-                final int N = mRecentTasks.size();
-                for (int i = 0; i < N; i++) {
-                    TaskRecord tr = mRecentTasks.get(i);
-                    // Skip tasks that do not match the caller.  We don't need to verify
-                    // callingPackage, because we are also limiting to callingUid and know
-                    // that will limit to the correct security sandbox.
-                    if (tr.effectiveUid != callingUid) {
-                        continue;
-                    }
-                    Intent intent = tr.getBaseIntent();
-                    if (intent == null ||
-                            !callingPackage.equals(intent.getComponent().getPackageName())) {
-                        continue;
-                    }
-                    ActivityManager.RecentTaskInfo taskInfo =
-                            createRecentTaskInfoFromTaskRecord(tr);
-                    AppTaskImpl taskImpl = new AppTaskImpl(taskInfo.persistentId, callingUid);
-                    list.add(taskImpl.asBinder());
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+        try {
+            synchronized(this) {
+                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
             }
-            return list;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -9851,58 +9815,6 @@
         return list;
     }
 
-    /**
-     * Creates a new RecentTaskInfo from a TaskRecord.
-     */
-    private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
-        // Update the task description to reflect any changes in the task stack
-        tr.updateTaskDescription();
-
-        // Compose the recent task info
-        ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-        rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
-        rti.persistentId = tr.taskId;
-        rti.baseIntent = new Intent(tr.getBaseIntent());
-        rti.origActivity = tr.origActivity;
-        rti.realActivity = tr.realActivity;
-        rti.description = tr.lastDescription;
-        rti.stackId = tr.getStackId();
-        rti.userId = tr.userId;
-        rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
-        rti.firstActiveTime = tr.firstActiveTime;
-        rti.lastActiveTime = tr.lastActiveTime;
-        rti.affiliatedTaskId = tr.mAffiliatedTaskId;
-        rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
-        rti.numActivities = 0;
-        if (tr.mBounds != null) {
-            rti.bounds = new Rect(tr.mBounds);
-        }
-        rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
-        rti.resizeMode = tr.mResizeMode;
-        rti.configuration.setTo(tr.getConfiguration());
-
-        ActivityRecord base = null;
-        ActivityRecord top = null;
-        ActivityRecord tmp;
-
-        for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
-            tmp = tr.mActivities.get(i);
-            if (tmp.finishing) {
-                continue;
-            }
-            base = tmp;
-            if (top == null || (top.state == ActivityState.INITIALIZING)) {
-                top = base;
-            }
-            rti.numActivities++;
-        }
-
-        rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
-        rti.topActivity = (top != null) ? top.intent.getComponent() : null;
-
-        return rti;
-    }
-
     private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
         boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
                 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
@@ -9936,118 +9848,15 @@
         final int callingUid = Binder.getCallingUid();
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
                 false, ALLOW_FULL_ONLY, "getRecentTasks", null);
+        final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+                callingUid);
+        final boolean detailed = checkCallingPermission(
+                android.Manifest.permission.GET_DETAILED_TASKS)
+                        == PackageManager.PERMISSION_GRANTED;
 
-        final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0;
-        final boolean withExcluded = (flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0;
         synchronized (this) {
-            final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+            return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
                     callingUid);
-            final boolean detailed = checkCallingPermission(
-                    android.Manifest.permission.GET_DETAILED_TASKS)
-                    == PackageManager.PERMISSION_GRANTED;
-
-            if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
-                Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
-                return ParceledListSlice.emptyList();
-            }
-            mRecentTasks.loadUserRecentsLocked(userId);
-
-            final int recentsCount = mRecentTasks.size();
-            ArrayList<ActivityManager.RecentTaskInfo> res =
-                    new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
-
-            final Set<Integer> includedUsers;
-            if (includeProfiles) {
-                includedUsers = mUserController.getProfileIds(userId);
-            } else {
-                includedUsers = new HashSet<>();
-            }
-            includedUsers.add(Integer.valueOf(userId));
-
-            for (int i = 0; i < recentsCount && maxNum > 0; i++) {
-                TaskRecord tr = mRecentTasks.get(i);
-                // Only add calling user or related users recent tasks
-                if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
-                    continue;
-                }
-
-                if (tr.realActivitySuspended) {
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
-                    continue;
-                }
-
-                // Return the entry if desired by the caller.  We always return
-                // the first entry, because callers always expect this to be the
-                // foreground app.  We may filter others if the caller has
-                // not supplied RECENT_WITH_EXCLUDED and there is some reason
-                // we should exclude the entry.
-
-                if (i == 0
-                        || withExcluded
-                        || (tr.intent == null)
-                        || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                                == 0)) {
-                    if (!allowed) {
-                        // If the caller doesn't have the GET_TASKS permission, then only
-                        // allow them to see a small subset of tasks -- their own and home.
-                        if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
-                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
-                            continue;
-                        }
-                    }
-                    final ActivityStack stack = tr.getStack();
-                    if ((flags & ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS) != 0) {
-                        if (stack != null && stack.isHomeOrRecentsStack()) {
-                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                    "Skipping, home or recents stack task: " + tr);
-                            continue;
-                        }
-                    }
-                    if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
-                        if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
-                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                    "Skipping, top task in docked stack: " + tr);
-                            continue;
-                        }
-                    }
-                    if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
-                        if (stack != null && stack.isPinnedStack()) {
-                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                    "Skipping, pinned stack task: " + tr);
-                            continue;
-                        }
-                    }
-                    if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
-                        // Don't include auto remove tasks that are finished or finishing.
-                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                "Skipping, auto-remove without activity: " + tr);
-                        continue;
-                    }
-                    if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
-                            && !tr.isAvailable) {
-                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                "Skipping, unavail real act: " + tr);
-                        continue;
-                    }
-
-                    if (!tr.mUserSetupComplete) {
-                        // Don't include task launched while user is not done setting-up.
-                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                                "Skipping, user setup not complete: " + tr);
-                        continue;
-                    }
-
-                    ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
-                    if (!detailed) {
-                        rti.baseIntent.replaceExtras((Bundle)null);
-                    }
-
-                    res.add(rti);
-                    maxNum--;
-                }
-            }
-            return new ParceledListSlice<>(res);
         }
     }
 
@@ -10119,23 +9928,10 @@
                 TaskRecord task = new TaskRecord(this,
                         mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
                         ainfo, intent, description);
-
-                int trimIdx = mRecentTasks.trimForTaskLocked(task, false);
-                if (trimIdx >= 0) {
-                    // If this would have caused a trim, then we'll abort because that
-                    // means it would be added at the end of the list but then just removed.
+                if (!mRecentTasks.addToBottom(task)) {
                     return INVALID_TASK_ID;
                 }
-
-                final int N = mRecentTasks.size();
-                if (N >= (ActivityManager.getMaxRecentTasksStatic()-1)) {
-                    final TaskRecord tr = mRecentTasks.remove(N - 1);
-                    tr.removedFromRecents();
-                }
-
-                task.inRecents = true;
-                mRecentTasks.add(task);
-                r.getStack().addTask(task, false, "addAppTask");
+                r.getStack().addTask(task, !ON_TOP, "addAppTask");
 
                 // TODO: Send the thumbnail to WM to store it.
 
@@ -10351,38 +10147,6 @@
         mWindowManager.executeAppTransition();
     }
 
-    private void removeTasksByPackageNameLocked(String packageName, int userId) {
-        // Remove all tasks with activities in the specified package from the list of recent tasks
-        for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
-            TaskRecord tr = mRecentTasks.get(i);
-            if (tr.userId != userId) continue;
-
-            ComponentName cn = tr.intent.getComponent();
-            if (cn != null && cn.getPackageName().equals(packageName)) {
-                // If the package name matches, remove the task.
-                mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
-            }
-        }
-    }
-
-    private void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
-            int userId) {
-
-        for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
-            TaskRecord tr = mRecentTasks.get(i);
-            if (userId != UserHandle.USER_ALL && tr.userId != userId) {
-                continue;
-            }
-
-            ComponentName cn = tr.intent.getComponent();
-            final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
-                    && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
-            if (sameComponent) {
-                mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
-            }
-        }
-    }
-
     @Override
     public void removeStack(int stackId) {
         enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -10609,7 +10373,7 @@
                 }
 
                 final ActivityStack stack = r.getStack();
-                if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
+                if (stack == null || !stack.inFreeformWindowingMode()) {
                     throw new IllegalStateException(
                             "exitFreeformMode: You can only go fullscreen from freeform.");
                 }
@@ -10677,27 +10441,20 @@
 
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                         + " to stackId=" + stackId + " toTop=" + toTop);
-                if (stackId == DOCKED_STACK_ID) {
-                    mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
-                            null /* initialBounds */);
-                }
 
-                ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
                 if (stack == null) {
-                    if (!isStaticStack(stackId)) {
-                        throw new IllegalStateException(
-                                "moveTaskToStack: No stack for stackId=" + stackId);
-                    }
-                    final ActivityDisplay display = task.getStack().getDisplay();
-                    final int windowingMode =
-                            getWindowingModeForStackId(stackId, display.hasSplitScreenStack());
-                    stack = display.getOrCreateStack(windowingMode,
-                            task.getStack().getActivityType(), toTop);
+                    throw new IllegalStateException(
+                            "moveTaskToStack: No stack for stackId=" + stackId);
                 }
                 if (!stack.isActivityTypeStandardOrUndefined()) {
                     throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
                             + taskId + " to stack " + stackId);
                 }
+                if (stack.inSplitScreenPrimaryWindowingMode()) {
+                    mWindowManager.setDockedStackCreateState(
+                            DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+                }
                 task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
                         "moveTaskToStack");
             } finally {
@@ -10769,7 +10526,7 @@
                 final ActivityStack stack =
                         mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
                 if (toTop) {
-                    mStackSupervisor.resizeStackLocked(stack.mStackId, null /* destBounds */,
+                    mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                             true /* preserveWindows */, true /* allowResizeInDockedMode */,
                             !DEFER_RESUME);
@@ -10862,7 +10619,12 @@
                     stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
                             animationDuration, false /* fromFullscreen */);
                 } else {
-                    mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
+                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                    if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                        return;
+                    }
+                    mStackSupervisor.resizeStackLocked(stack, destBounds, null /* tempTaskBounds */,
                             null /* tempTaskInsetBounds */, preserveWindows,
                             allowResizeInDockedMode, !DEFER_RESUME);
                 }
@@ -12938,6 +12700,10 @@
                 throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
                         + bugreportType);
         }
+        // Always log caller, even if it does not have permission to dump.
+        String type = extraOptions == null ? "bugreport" : extraOptions;
+        Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
+
         enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
         if (extraOptions != null) {
             SystemProperties.set("dumpstate.options", extraOptions);
@@ -14145,7 +13911,6 @@
 
             // Load resources only after the current configuration has been set.
             final Resources res = mContext.getResources();
-            mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
             mThumbnailWidth = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.thumbnail_width);
             mThumbnailHeight = res.getDimensionPixelSize(
@@ -15133,7 +14898,9 @@
                 }
             } else if ("recents".equals(cmd) || "r".equals(cmd)) {
                 synchronized (this) {
-                    dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage);
+                    if (mRecentTasks != null) {
+                        mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
+                    }
                 }
             } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
                 String[] newArgs;
@@ -15354,7 +15121,9 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                if (mRecentTasks != null) {
+                    mRecentTasks.dump(pw, dumpAll, dumpPackage);
+                }
                 pw.println();
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
@@ -15424,7 +15193,9 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                if (mRecentTasks != null) {
+                    mRecentTasks.dump(pw, dumpAll, dumpPackage);
+                }
                 pw.println();
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
@@ -15510,42 +15281,6 @@
         }
     }
 
-    void dumpRecentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, String dumpPackage) {
-        pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
-
-        boolean printedAnything = false;
-
-        if (mRecentTasks != null && mRecentTasks.size() > 0) {
-            boolean printedHeader = false;
-
-            final int N = mRecentTasks.size();
-            for (int i=0; i<N; i++) {
-                TaskRecord tr = mRecentTasks.get(i);
-                if (dumpPackage != null) {
-                    if (tr.realActivity == null ||
-                            !dumpPackage.equals(tr.realActivity.getPackageName())) {
-                        continue;
-                    }
-                }
-                if (!printedHeader) {
-                    pw.println("  Recent tasks:");
-                    printedHeader = true;
-                    printedAnything = true;
-                }
-                pw.print("  * Recent #"); pw.print(i); pw.print(": ");
-                        pw.println(tr);
-                if (dumpAll) {
-                    mRecentTasks.get(i).dump(pw, "    ");
-                }
-            }
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
-    }
-
     void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
         pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
@@ -19360,7 +19095,7 @@
                                         // Remove all permissions granted from/to this package
                                         removeUriPermissionsForPackageLocked(ssp, userId, true);
 
-                                        removeTasksByPackageNameLocked(ssp, userId);
+                                        mRecentTasks.removeTasksByPackageName(ssp, userId);
 
                                         mServices.forceStopPackageLocked(ssp, userId);
 
@@ -20697,9 +20432,10 @@
     /** Helper method that requests bounds from WM and applies them to stack. */
     private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
         final Rect newStackBounds = new Rect();
-        mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds);
+        final ActivityStack stack = mStackSupervisor.getStack(stackId);
+        stack.getBoundsForNewConfiguration(newStackBounds);
         mStackSupervisor.resizeStackLocked(
-                stackId, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+                stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
                 null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                 false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
     }
@@ -24367,125 +24103,6 @@
     }
 
     /**
-     * An implementation of IAppTask, that allows an app to manage its own tasks via
-     * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
-     * only the process that calls getAppTasks() can call the AppTask methods.
-     */
-    class AppTaskImpl extends IAppTask.Stub {
-        private int mTaskId;
-        private int mCallingUid;
-
-        public AppTaskImpl(int taskId, int callingUid) {
-            mTaskId = taskId;
-            mCallingUid = callingUid;
-        }
-
-        private void checkCaller() {
-            if (mCallingUid != Binder.getCallingUid()) {
-                throw new SecurityException("Caller " + mCallingUid
-                        + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
-            }
-        }
-
-        @Override
-        public void finishAndRemoveTask() {
-            checkCaller();
-
-            synchronized (ActivityManagerService.this) {
-                long origId = Binder.clearCallingIdentity();
-                try {
-                    // We remove the task from recents to preserve backwards
-                    if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
-                            REMOVE_FROM_RECENTS)) {
-                        throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(origId);
-                }
-            }
-        }
-
-        @Override
-        public ActivityManager.RecentTaskInfo getTaskInfo() {
-            checkCaller();
-
-            synchronized (ActivityManagerService.this) {
-                long origId = Binder.clearCallingIdentity();
-                try {
-                    TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
-                    if (tr == null) {
-                        throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                    }
-                    return createRecentTaskInfoFromTaskRecord(tr);
-                } finally {
-                    Binder.restoreCallingIdentity(origId);
-                }
-            }
-        }
-
-        @Override
-        public void moveToFront() {
-            checkCaller();
-            // Will bring task to front if it already has a root activity.
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                synchronized (this) {
-                    mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-
-        @Override
-        public int startActivity(IBinder whoThread, String callingPackage,
-                Intent intent, String resolvedType, Bundle bOptions) {
-            checkCaller();
-
-            int callingUser = UserHandle.getCallingUserId();
-            TaskRecord tr;
-            IApplicationThread appThread;
-            synchronized (ActivityManagerService.this) {
-                tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
-                if (tr == null) {
-                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                }
-                appThread = IApplicationThread.Stub.asInterface(whoThread);
-                if (appThread == null) {
-                    throw new IllegalArgumentException("Bad app thread " + appThread);
-                }
-            }
-            return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent,
-                    resolvedType, null, null, null, null, 0, 0, null, null,
-                    null, bOptions, false, callingUser, tr, "AppTaskImpl");
-        }
-
-        @Override
-        public void setExcludeFromRecents(boolean exclude) {
-            checkCaller();
-
-            synchronized (ActivityManagerService.this) {
-                long origId = Binder.clearCallingIdentity();
-                try {
-                    TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
-                    if (tr == null) {
-                        throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                    }
-                    Intent intent = tr.getBaseIntent();
-                    if (exclude) {
-                        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                    } else {
-                        intent.setFlags(intent.getFlags()
-                                & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(origId);
-                }
-            }
-        }
-    }
-
-    /**
      * Kill processes for the user with id userId and that depend on the package named packageName
      */
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4c93423..f03d2d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -73,10 +73,8 @@
 
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -86,15 +84,6 @@
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
 
-    // Is the object moving in a positive direction?
-    private static final boolean MOVING_FORWARD = true;
-    // Is the object moving in the horizontal plan?
-    private static final boolean MOVING_HORIZONTALLY = true;
-    // Is the object current point great then its target point?
-    private static final boolean GREATER_THAN_TARGET = true;
-    // Amount we reduce the stack size by when testing a task re-size.
-    private static final int STACK_BOUNDS_INSET = 10;
-
     // IPC interface to activity manager -- don't need to do additional security checks.
     final IActivityManager mInterface;
 
@@ -1944,8 +1933,6 @@
                 return runStackInfo(pw);
             case "move-top-activity-to-pinned-stack":
                 return runMoveTopActivityToPinnedStack(pw);
-            case "size-docked-stack-test":
-                return runStackSizeDockedStackTest(pw);
             case "remove":
                 return runStackRemove(pw);
             default:
@@ -2143,89 +2130,6 @@
         return 0;
     }
 
-    int runStackSizeDockedStackTest(PrintWriter pw) throws RemoteException {
-        final PrintWriter err = getErrPrintWriter();
-        final int stepSize = Integer.parseInt(getNextArgRequired());
-        final String side = getNextArgRequired();
-        final String delayStr = getNextArg();
-        final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
-
-        ActivityManager.StackInfo info = mInterface.getStackInfo(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
-        if (info == null) {
-            err.println("Docked stack doesn't exist");
-            return -1;
-        }
-        if (info.bounds == null) {
-            err.println("Docked stack doesn't have a bounds");
-            return -1;
-        }
-        Rect bounds = info.bounds;
-
-        final boolean horizontalGrowth = "l".equals(side) || "r".equals(side);
-        final int changeSize = (horizontalGrowth ? bounds.width() : bounds.height()) / 2;
-        int currentPoint;
-        switch (side) {
-            case "l":
-                currentPoint = bounds.left;
-                break;
-            case "r":
-                currentPoint = bounds.right;
-                break;
-            case "t":
-                currentPoint = bounds.top;
-                break;
-            case "b":
-                currentPoint = bounds.bottom;
-                break;
-            default:
-                err.println("Unknown growth side: " + side);
-                return -1;
-        }
-
-        final int startPoint = currentPoint;
-        final int minPoint = currentPoint - changeSize;
-        final int maxPoint = currentPoint + changeSize;
-
-        int maxChange;
-        pw.println("Shrinking docked stack side=" + side);
-        pw.flush();
-        while (currentPoint > minPoint) {
-            maxChange = Math.min(stepSize, currentPoint - minPoint);
-            currentPoint -= maxChange;
-            setBoundsSide(bounds, side, currentPoint);
-            int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
-            if (res < 0) {
-                return res;
-            }
-        }
-
-        pw.println("Growing docked stack side=" + side);
-        pw.flush();
-        while (currentPoint < maxPoint) {
-            maxChange = Math.min(stepSize, maxPoint - currentPoint);
-            currentPoint += maxChange;
-            setBoundsSide(bounds, side, currentPoint);
-            int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
-            if (res < 0) {
-                return res;
-            }
-        }
-
-        pw.println("Back to Original size side=" + side);
-        pw.flush();
-        while (currentPoint > startPoint) {
-            maxChange = Math.min(stepSize, currentPoint - startPoint);
-            currentPoint -= maxChange;
-            setBoundsSide(bounds, side, currentPoint);
-            int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
-            if (res < 0) {
-                return res;
-            }
-        }
-        return 0;
-    }
-
     void setBoundsSide(Rect bounds, String side, int value) {
         switch (side) {
             case "l":
@@ -2687,10 +2591,6 @@
             pw.println("           Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
             pw.println("           and supplying temporary different task bounds indicated by");
             pw.println("           <TASK_LEFT,TOP,RIGHT,BOTTOM>");
-            pw.println("       size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]");
-            pw.println("           Test command for sizing docked stack by");
-            pw.println("           <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom");
-            pw.println("           applying the optional [DELAY_MS] between each step.");
             pw.println("       move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
             pw.println("           Moves the top activity from");
             pw.println("           <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index fdcb8c6..93c0f77 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -5,6 +5,7 @@
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -127,7 +128,7 @@
             case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
                 mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
                 break;
-            case WINDOW_STATE_FREEFORM:
+            case WINDOWING_MODE_FREEFORM:
                 mWindowState = WINDOW_STATE_FREEFORM;
                 break;
             default:
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7b0b942..f544d37 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,9 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
@@ -661,8 +659,7 @@
             return;
         }
 
-        final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
-                (targetStackBounds != null);
+        final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
         if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
             // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
             // update that here in order
@@ -684,10 +681,6 @@
         }
     }
 
-    boolean isFreeform() {
-        return task != null && task.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
-    }
-
     @Override
     protected int getChildCount() {
         // {@link ActivityRecord} is a leaf node and has no children.
@@ -948,7 +941,7 @@
         // update the initial multi-window modes so that the callbacks are scheduled correctly when
         // the user leaves that mode.
         mLastReportedMultiWindowMode = !task.mFullscreen;
-        mLastReportedPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID);
+        mLastReportedPictureInPictureMode = inPinnedWindowingMode();
     }
 
     void removeWindowContainer() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1940ca2..3d3bae3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,27 +16,25 @@
 
 package com.android.server.am;
 
-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.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-
 import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.am.ActivityDisplay.POSITION_TOP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -100,16 +98,19 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackId;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
 import android.app.ResultInfo;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
@@ -192,10 +193,6 @@
     // finished destroying itself.
     private static final int DESTROY_TIMEOUT = 10 * 1000;
 
-    // How long until we reset a task when the user returns to it.  Currently
-    // disabled.
-    private static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
-
     // Set to false to disable the preview that is shown while a new activity
     // is being started.
     private static final boolean SHOW_APP_STARTING_PREVIEW = true;
@@ -352,12 +349,11 @@
     private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
     private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
     private final Rect mTmpRect2 = new Rect();
+    private final Point mTmpSize = new Point();
 
     /** Run all ActivityStacks through this */
     protected final ActivityStackSupervisor mStackSupervisor;
 
-    private final LaunchingTaskPositioner mTaskPositioner;
-
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
 
@@ -460,8 +456,6 @@
         mWindowManager = mService.mWindowManager;
         mStackId = stackId;
         mCurrentUser = mService.mUserController.getCurrentUserId();
-        mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
-                ? new LaunchingTaskPositioner() : null;
         mTmpRect2.setEmpty();
         setWindowingMode(windowingMode);
         setActivityType(activityType);
@@ -502,14 +496,11 @@
         mDisplayId = activityDisplay.mDisplayId;
         mBounds = bounds != null ? new Rect(bounds) : null;
         mFullscreen = mBounds == null;
-        if (mTaskPositioner != null) {
-            mTaskPositioner.setDisplay(activityDisplay.mDisplay);
-            mTaskPositioner.configure(mBounds);
-        }
+
         onParentChanged();
 
         activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
-        if (mStackId == DOCKED_STACK_ID) {
+        if (inSplitScreenPrimaryWindowingMode()) {
             // If we created a docked stack we want to resize it so it resizes all other stacks
             // in the system.
             mStackSupervisor.resizeDockedStackLocked(
@@ -533,9 +524,6 @@
             display.removeChild(this);
         }
         mDisplayId = INVALID_DISPLAY;
-        if (mTaskPositioner != null) {
-            mTaskPositioner.reset();
-        }
     }
 
     /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
@@ -639,9 +627,6 @@
 
     void setBounds(Rect bounds) {
         mBounds = mFullscreen ? null : new Rect(bounds);
-        if (mTaskPositioner != null) {
-            mTaskPositioner.configure(bounds);
-        }
     }
 
     ActivityRecord topRunningActivityLocked() {
@@ -818,14 +803,6 @@
         return isActivityTypeHome() || isActivityTypeRecents();
     }
 
-    final boolean isDockedStack() {
-        return mStackId == DOCKED_STACK_ID;
-    }
-
-    final boolean isPinnedStack() {
-        return mStackId == PINNED_STACK_ID;
-    }
-
     final boolean isOnHomeDisplay() {
         return mDisplayId == DEFAULT_DISPLAY;
     }
@@ -1505,9 +1482,9 @@
      * needed. A stack is considered translucent if it don't contain a visible or
      * starting (about to be visible) activity that is fullscreen (opaque).
      * @param starting The currently starting activity or null if there is none.
-     * @param stackBehindId The id of the stack directly behind this one.
+     * @param stackBehind The stack directly behind this one.
      */
-    private boolean isStackTranslucent(ActivityRecord starting, int stackBehindId) {
+    private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1532,7 +1509,6 @@
                     return false;
                 }
 
-                final ActivityStack stackBehind = mStackSupervisor.getStack(stackBehindId);
                 final boolean stackBehindHomeOrRecent = stackBehind != null
                         && stackBehind.isHomeOrRecentsStack();
                 if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
@@ -1579,7 +1555,6 @@
 
         // Check position and visibility of this stack relative to the front stack on its display.
         final ActivityStack topStack = getTopStackOnDisplay();
-        final int topStackId = topStack.mStackId;
         final int windowingMode = getWindowingMode();
         final int activityType = getActivityType();
 
@@ -1587,7 +1562,7 @@
             // If the assistant stack is focused and translucent, then the docked stack is always
             // visible
             if (topStack.isActivityTypeAssistant()) {
-                return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
+                return topStack.isStackTranslucent(starting, this);
             }
             return true;
         }
@@ -1612,18 +1587,16 @@
         }
         final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
                 ? displayStacks.get(stackBehindTopIndex) : null;
-        int stackBehindTopId = INVALID_STACK_ID;
         int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
         int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
         if (stackBehindTop != null) {
-            stackBehindTopId = stackBehindTop.mStackId;
             stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
             stackBehindTopActivityType = stackBehindTop.getActivityType();
         }
 
         final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
         if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
-            if (stackIndex == stackBehindTopIndex) {
+            if (this == stackBehindTop) {
                 // Stacks directly behind the docked or pinned stack are always visible.
                 return true;
             } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
@@ -1632,14 +1605,13 @@
                 if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                     return true;
                 } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
-                    return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
-                            starting, mStackId);
+                    return stackBehindTop.isStackTranslucent(starting, this);
                 }
             }
         }
 
         if (topStack.isBackdropToTranslucentActivity()
-                && topStack.isStackTranslucent(starting, stackBehindTopId)) {
+                && topStack.isStackTranslucent(starting, stackBehindTop)) {
             // Stacks behind the fullscreen or assistant stack with a translucent activity are
             // always visible so they can act as a backdrop to the translucent activity.
             // For example, dialog activities
@@ -1657,9 +1629,9 @@
             }
         }
 
-        if (StackId.isStaticStack(mStackId)
-                || isHomeOrRecentsStack() || isActivityTypeAssistant()) {
-            // Visibility of any static stack should have been determined by the conditions above.
+        if (isOnHomeDisplay()) {
+            // Visibility of any stack on default display should have been determined by the
+            // conditions above.
             return false;
         }
 
@@ -1675,7 +1647,7 @@
                 return false;
             }
 
-            if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
+            if (!stack.isStackTranslucent(starting, null /* stackBehind */)) {
                 return false;
             }
         }
@@ -1801,7 +1773,8 @@
                         makeInvisible(r);
                     }
                 }
-                if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                final int windowingMode = getWindowingMode();
+                if (windowingMode == WINDOWING_MODE_FREEFORM) {
                     // The visibility of tasks and the activities they contain in freeform stack are
                     // determined individually unlike other stacks where the visibility or fullscreen
                     // status of an activity in a previous task affects other.
@@ -1816,7 +1789,8 @@
                     // show activities in the next application stack behind them vs. another
                     // task in the home stack like recents.
                     behindFullscreenActivity = true;
-                } else if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+                } else if (windowingMode == WINDOWING_MODE_FULLSCREEN
+                        || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
                     if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
                             + " returning to non-application type=" + task.getTaskToReturnTo());
                     // Once we reach a fullscreen stack task that has a running activity and should
@@ -1852,6 +1826,32 @@
     }
 
     /**
+     * Returns true if this stack should be resized to match the bounds specified by
+     * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
+     */
+    boolean resizeStackWithLaunchBounds() {
+        return inPinnedWindowingMode();
+    }
+
+    /**
+     * Returns true if we try to maintain focus in the current stack when the top activity finishes.
+     */
+    private boolean keepFocusInStackIfPossible() {
+        final int windowingMode = getWindowingMode();
+        return windowingMode == WINDOWING_MODE_FREEFORM
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || windowingMode == WINDOWING_MODE_PINNED;
+    }
+
+    /**
+     * Returns true if the top task in the task is allowed to return home when finished and
+     * there are other tasks in the stack.
+     */
+    boolean allowTopTaskToReturnHome() {
+        return !inPinnedWindowingMode();
+    }
+
+    /**
      * @return the top most visible activity that wants to dismiss Keyguard
      */
     ActivityRecord getTopDismissingKeyguardActivity() {
@@ -1867,7 +1867,7 @@
      */
     boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
             boolean isTop) {
-        final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
+        final boolean isInPinnedStack = r.inPinnedWindowingMode();
         final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
                 mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
         final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
@@ -2165,7 +2165,7 @@
         mResumedActivity = r;
         r.state = ActivityState.RESUMED;
         mService.setResumedActivityUncheckLocked(r, reason);
-        mStackSupervisor.addRecentActivity(r);
+        mStackSupervisor.mRecentTasks.add(r.getTask());
     }
 
     private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
@@ -2617,7 +2617,7 @@
 
     private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
             ActivityOptions options, String reason) {
-        if ((!mFullscreen || !isOnHomeDisplay()) && adjustFocusToNextFocusableStackLocked(reason)) {
+        if (adjustFocusToNextFocusableStackLocked(reason)) {
             // Try to move focus to the next visible stack with a running activity if this
             // stack is not covering the entire screen or is on a secondary display (with no home
             // stack).
@@ -2746,9 +2746,8 @@
             // make underlying task focused when this one will be finished.
             int returnToType = isLastTaskOverHome
                     ? task.getTaskToReturnTo() : ACTIVITY_TYPE_STANDARD;
-            if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
-                returnToType = topTask == null
-                        ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
+            if (fromHomeOrRecents && allowTopTaskToReturnHome()) {
+                returnToType = topTask == null ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
             }
             task.setTaskToReturnTo(returnToType);
         }
@@ -2901,7 +2900,7 @@
             // Ensure the caller has requested not to trigger auto-enter PiP
             return false;
         }
-        if (pipCandidate == null || pipCandidate.getStackId() == PINNED_STACK_ID) {
+        if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
             // Ensure that we do not trigger entering PiP an activity on the pinned stack
             return false;
         }
@@ -3186,15 +3185,8 @@
 
     final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
             ActivityRecord newActivity) {
-        boolean forceReset =
+        final boolean forceReset =
                 (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
-        if (ACTIVITY_INACTIVE_RESET_TIME > 0
-                && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
-            if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
-                forceReset = true;
-            }
-        }
-
         final TaskRecord task = taskTop.getTask();
 
         /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
@@ -3291,7 +3283,7 @@
         final String myReason = reason + " adjustFocus";
 
         if (next != r) {
-            if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) {
+            if (next != null && keepFocusInStackIfPossible() && isFocusable()) {
                 // For freeform, docked, and pinned stacks we always keep the focus within the
                 // stack as long as there is a running activity.
                 return;
@@ -3757,7 +3749,7 @@
 
         if (mode == FINISH_IMMEDIATELY
                 || (prevState == ActivityState.PAUSED
-                    && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
+                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
                 || finishingActivityInNonFocusedStack
                 || prevState == STOPPING
                 || prevState == STOPPED
@@ -4464,7 +4456,9 @@
         // Don't refocus if invisible to current user
         final ActivityRecord top = tr.getTopActivity();
         if (top == null || !top.okToShowLocked()) {
-            mStackSupervisor.addRecentActivity(top);
+            if (top != null) {
+                mStackSupervisor.mRecentTasks.add(top.getTask());
+            }
             ActivityOptions.abort(options);
             return;
         }
@@ -4571,8 +4565,8 @@
             mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
         }
 
-        if (mStackId == PINNED_STACK_ID) {
-            mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
+        if (inPinnedWindowingMode()) {
+            mStackSupervisor.removeStackLocked(mStackId);
             return true;
         }
 
@@ -4687,7 +4681,7 @@
         for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
             final TaskRecord task = mTaskHistory.get(i);
             if (task.isResizeable()) {
-                if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                if (inFreeformWindowingMode()) {
                     // 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.
@@ -4889,7 +4883,7 @@
             if (focusedStack && topTask) {
                 // Give the latest time to ensure foreground task can be sorted
                 // at the first, because lastActiveTime of creating task is 0.
-                ci.lastActiveTime = System.currentTimeMillis();
+                ci.lastActiveTime = SystemClock.elapsedRealtime();
                 topTask = false;
             }
 
@@ -5069,7 +5063,7 @@
             if (task.autoRemoveFromRecents() || isVoiceSession) {
                 // Task creator asked to remove this when done, or this task was a voice
                 // interaction, so it should not remain on the recent tasks list.
-                mStackSupervisor.removeTaskFromRecents(task);
+                mStackSupervisor.mRecentTasks.remove(task);
             }
 
             task.removeWindowContainer();
@@ -5097,7 +5091,7 @@
         task.setStack(null);
 
         // Notify if a task from the pinned stack is being removed (or moved depending on the mode)
-        if (mStackId == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             mService.mTaskChangeNotificationController.notifyActivityUnpinned();
         }
     }
@@ -5120,10 +5114,12 @@
     }
 
     boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
-        if (mTaskPositioner == null) {
+        if (!task.inFreeformWindowingMode()) {
             return false;
         }
-        mTaskPositioner.updateDefaultBounds(task, mTaskHistory, windowLayout);
+        mStackSupervisor.getLaunchingTaskPositioner()
+                .updateDefaultBounds(task, mTaskHistory, windowLayout);
+
         return true;
     }
 
@@ -5248,7 +5244,9 @@
     @Override
     public String toString() {
         return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
-                + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
+                + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
+                + " mode=" + windowingModeToString(getWindowingMode()) + ", "
+                + mTaskHistory.size() + " tasks}";
     }
 
     void onLockTaskPackagesUpdatedLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index da2827a..5221afd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,11 +21,7 @@
 import static android.Manifest.permission.START_ANY_ACTIVITY;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -35,10 +31,13 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -47,6 +46,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
+
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
@@ -86,12 +86,13 @@
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
+
 import static java.lang.Integer.MAX_VALUE;
 
 import android.Manifest;
@@ -102,7 +103,6 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
@@ -176,7 +176,8 @@
 import java.util.List;
 import java.util.Set;
 
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener {
+public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
+        RecentTasks.Callbacks {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
     private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
@@ -286,7 +287,7 @@
 
     final ActivityManagerService mService;
 
-    private RecentTasks mRecentTasks;
+    RecentTasks mRecentTasks;
 
     final ActivityStackSupervisorHandler mHandler;
 
@@ -294,8 +295,10 @@
     WindowManagerService mWindowManager;
     DisplayManager mDisplayManager;
 
+    LaunchingTaskPositioner mTaskPositioner = new LaunchingTaskPositioner();
+
     /** Counter for next free stack ID to use for dynamic activity stacks. */
-    private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
+    private int mNextFreeStackId = 0;
 
     /**
      * Maps the task identifier that activities are currently being started in to the userId of the
@@ -576,6 +579,7 @@
 
     void setRecentTasks(RecentTasks recentTasks) {
         mRecentTasks = recentTasks;
+        mRecentTasks.registerCallback(this);
     }
 
     /**
@@ -749,7 +753,7 @@
         // Otherwise, check the recent tasks and return if we find it there and we are not restoring
         // the task from recents
         if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        final TaskRecord task = mRecentTasks.taskForIdLocked(id);
+        final TaskRecord task = mRecentTasks.getTask(id);
 
         if (task == null) {
             if (DEBUG_RECENTS) {
@@ -858,7 +862,7 @@
         // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
         // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
         int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
-        while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
+        while (mRecentTasks.containsTaskId(candidateTaskId, userId)
                 || anyTaskForIdLocked(
                         candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
             candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
@@ -2105,8 +2109,8 @@
                 // moveTaskToStackUncheckedLocked() should already placed the task on top,
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
-            if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
-                resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
+            if (stack.resizeStackWithLaunchBounds()) {
+                resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
                         null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                         true /* allowResizeInDockedMode */, !DEFER_RESUME);
             } else {
@@ -2139,6 +2143,10 @@
                 || mService.mSupportsFreeformWindowManagement;
     }
 
+    LaunchingTaskPositioner getLaunchingTaskPositioner() {
+        return mTaskPositioner;
+    }
+
     protected <T extends ActivityStack> T getStack(int stackId) {
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
             final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
@@ -2417,7 +2425,7 @@
             case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
         }
 
-        if (StackId.isDynamicStack(stack.mStackId)) {
+        if (!stack.isOnHomeDisplay()) {
             return r.canBeLaunchedOnDisplay(displayId);
         }
         Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
@@ -2511,18 +2519,15 @@
         return null;
     }
 
-    void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
-            boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
-        if (stackId == DOCKED_STACK_ID) {
+    void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+            boolean deferResume) {
+
+        if (stack.inSplitScreenPrimaryWindowingMode()) {
             resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
                     preserveWindows, deferResume);
             return;
         }
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-            return;
-        }
 
         final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack();
         if (!allowResizeInDockedMode
@@ -2532,7 +2537,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
         mWindowManager.deferSurfaceLayout();
         try {
             if (stack.supportsSplitScreenWindowingMode()) {
@@ -2584,8 +2589,7 @@
 
     /**
      * TODO: This should just change the windowing mode and resize vs. actually moving task around.
-     * Can do that once we are no longer using static stack ids. Specially when
-     * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
+     * Can do that once we are no longer using static stack ids.
      */
     private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
             int toDisplayId, boolean onTop) {
@@ -2606,7 +2610,7 @@
                     if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                         continue;
                     }
-                    resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
+                    resizeStackLocked(otherStack, null, null, null, PRESERVE_WINDOWS,
                             true /* allowResizeInDockedMode */, DEFER_RESUME);
                 }
 
@@ -2744,7 +2748,7 @@
                             tempRect /* outStackBounds */,
                             otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
 
-                    resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
+                    resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
                             !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
                             tempOtherTaskInsetBounds, preserveWindows,
                             true /* allowResizeInDockedMode */, deferResume);
@@ -2892,23 +2896,9 @@
         return false;
     }
 
-    void addRecentActivity(ActivityRecord r) {
-        if (r == null) {
-            return;
-        }
-        final TaskRecord task = r.getTask();
-        mRecentTasks.addLocked(task);
-        task.touchActiveTime();
-    }
-
-    void removeTaskFromRecents(TaskRecord task) {
-        mRecentTasks.remove(task);
-        task.removedFromRecents();
-    }
-
     void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
         if (removeFromRecents) {
-            removeTaskFromRecents(tr);
+            mRecentTasks.remove(tr);
         }
         ComponentName component = tr.getBaseIntent().getComponent();
         if (component == null) {
@@ -2979,8 +2969,7 @@
 
     int getNextStackId() {
         while (true) {
-            if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
-                    && getStack(mNextFreeStackId) == null) {
+            if (getStack(mNextFreeStackId) == null) {
                 break;
             }
             mNextFreeStackId++;
@@ -2989,7 +2978,8 @@
     }
 
     /**
-     * Restores a recent task to a stack
+     * Called to restore the state of the task into the stack that it's supposed to go into.
+     *
      * @param task The recent task to be restored.
      * @param aOptions The activity options to use for restoration.
      * @return true if the task has been restored successfully.
@@ -3020,6 +3010,22 @@
         return true;
     }
 
+    @Override
+    public void onRecentTaskAdded(TaskRecord task) {
+        task.touchActiveTime();
+    }
+
+    @Override
+    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+        if (wasTrimmed) {
+            // Task was trimmed from the recent tasks list -- remove the active task record as well
+            // since the user won't really be able to go back to it
+            removeTaskByIdLocked(task.taskId, false /* killProcess */,
+                    false /* removeFromRecents */);
+        }
+        task.removedFromRecents();
+    }
+
     /**
      * Move stack with all its existing content to specified display.
      * @param stackId Id of stack to move.
@@ -3160,7 +3166,7 @@
             // 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.
-            resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
+            resizeStackLocked(stack, task.mBounds, null /* tempTaskBounds */,
                     null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                     true /* allowResizeInDockedMode */, !DEFER_RESUME);
 
@@ -3500,7 +3506,7 @@
         final ActivityStack stack = task.getStack();
 
         r.mLaunchTaskBehind = false;
-        mRecentTasks.addLocked(task);
+        mRecentTasks.add(task);
         mService.mTaskChangeNotificationController.notifyTaskStackChanged();
         r.setVisibility(false);
 
@@ -3653,7 +3659,7 @@
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
         // appropriate.
-        removeStackLocked(PINNED_STACK_ID);
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
         final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
@@ -3896,7 +3902,9 @@
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 pw.println();
-                pw.println("  Stack #" + stack.mStackId + ":");
+                pw.println("  Stack #" + stack.mStackId
+                        + ": type=" + activityTypeToString(getActivityType())
+                        + " mode=" + windowingModeToString(getWindowingMode()));
                 pw.println("  mFullscreen=" + stack.mFullscreen);
                 pw.println("  isSleeping=" + stack.shouldSleepActivities());
                 pw.println("  mBounds=" + stack.mBounds);
@@ -4315,8 +4323,8 @@
                 // The task landed on an inappropriate display somehow, move it to the default
                 // display.
                 // TODO(multi-display): Find proper stack for the task on the default display.
-                mService.moveTaskToStack(task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
-                        true /* toTop */);
+                mService.setTaskWindowingMode(task.taskId,
+                        WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
                 launchOnSecondaryDisplayFailed = true;
             } else {
                 // The task might have landed on a display different from requested.
@@ -4402,7 +4410,7 @@
     void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
         final ActivityStack stack = task.getStack();
         if (prevStack == null || prevStack == stack
-                || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
+                || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d444c66..cceb576 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -26,11 +26,6 @@
 import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.isDynamicStack;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -610,15 +605,19 @@
             mSupervisor.reportTaskToFrontNoLaunch(mStartActivity);
         }
 
-        int startedActivityStackId = INVALID_STACK_ID;
+        ActivityStack startedActivityStack = null;
         final ActivityStack currentStack = r.getStack();
         if (currentStack != null) {
-            startedActivityStackId = currentStack.mStackId;
+            startedActivityStack = currentStack;
         } else if (mTargetStack != null) {
-            startedActivityStackId = targetStack.mStackId;
+            startedActivityStack = targetStack;
         }
 
-        if (startedActivityStackId == DOCKED_STACK_ID) {
+        if (startedActivityStack == null) {
+            return;
+        }
+
+        if (startedActivityStack.inSplitScreenPrimaryWindowingMode()) {
             final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack(
                             WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
             final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
@@ -634,14 +633,14 @@
 
         boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                 == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null);
-        if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
-                || result == START_DELIVERED_TO_TOP || clearedTask)) {
+        if (startedActivityStack.inPinnedWindowingMode()
+                && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP
+                || clearedTask)) {
             // 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(
                     clearedTask);
-            return;
         }
     }
 
@@ -1240,8 +1239,8 @@
                 mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                         mOptions);
             }
-        } else {
-            mSupervisor.addRecentActivity(mStartActivity);
+        } else if (mStartActivity != null) {
+            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
         }
         mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
 
@@ -1427,7 +1426,7 @@
             // in any task/stack, however it could launch other activities like ResolverActivity,
             // and we want those to stay in the original task.
             if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
-                    && mSourceRecord.isFreeform())  {
+                    && mSourceRecord.inFreeformWindowingMode())  {
                 mAddingToTask = true;
             }
         }
@@ -2001,9 +2000,9 @@
             return;
         }
 
-        final int stackId = task.getStackId();
-        if (StackId.resizeStackWithLaunchBounds(stackId)) {
-            mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+        final ActivityStack stack = task.getStack();
+        if (stack != null && stack.resizeStackWithLaunchBounds()) {
+            mService.resizeStack(stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
         } else {
             task.updateOverrideConfiguration(bounds);
         }
@@ -2119,7 +2118,7 @@
                     mSupervisor.getStacksOnDefaultDisplay();
             for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 stack = homeDisplayStacks.get(stackNdx);
-                if (isDynamicStack(stack.mStackId)) {
+                if (!stack.isOnHomeDisplay()) {
                     if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                             "computeStackFocus: Setting focused stack=" + stack);
                     return stack;
@@ -2138,7 +2137,6 @@
     private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
         final ActivityStack focusedStack = mSupervisor.mFocusedStack;
         final boolean canUseFocusedStack;
-        final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
         if (focusedStack.isActivityTypeAssistant()) {
             canUseFocusedStack = r.isActivityTypeAssistant();
         } else {
@@ -2161,7 +2159,7 @@
                 default:
                     // Dynamic stacks behave similarly to the fullscreen stack and can contain any
                     // resizeable task.
-                    canUseFocusedStack = isDynamicStack(focusedStackId)
+                    canUseFocusedStack = !focusedStack.isOnHomeDisplay()
                             && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
             }
         }
@@ -2205,7 +2203,7 @@
                 return mSupervisor.mFocusedStack;
             }
 
-            if (parentStack != null && parentStack.isDockedStack()) {
+            if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
                 // If parent was in docked stack, the natural place to launch another activity
                 // will be fullscreen, so it can appear alongside the docked window.
                 final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
new file mode 100644
index 0000000..a4e2e70
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+
+import android.app.ActivityManager;
+import android.app.IAppTask;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.UserHandle;
+
+/**
+ * An implementation of IAppTask, that allows an app to manage its own tasks via
+ * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
+ * only the process that calls getAppTasks() can call the AppTask methods.
+ */
+class AppTaskImpl extends IAppTask.Stub {
+    private ActivityManagerService mService;
+
+    private int mTaskId;
+    private int mCallingUid;
+
+    public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) {
+        mService = service;
+        mTaskId = taskId;
+        mCallingUid = callingUid;
+    }
+
+    private void checkCaller() {
+        if (mCallingUid != Binder.getCallingUid()) {
+            throw new SecurityException("Caller " + mCallingUid
+                    + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
+        }
+    }
+
+    @Override
+    public void finishAndRemoveTask() {
+        checkCaller();
+
+        synchronized (mService) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                // We remove the task from recents to preserve backwards
+                if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                        REMOVE_FROM_RECENTS)) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public ActivityManager.RecentTaskInfo getTaskInfo() {
+        checkCaller();
+
+        synchronized (mService) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+                if (tr == null) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+                return RecentTasks.createRecentTaskInfo(tr);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void moveToFront() {
+        checkCaller();
+        // Will bring task to front if it already has a root activity.
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                mService.mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int startActivity(IBinder whoThread, String callingPackage,
+            Intent intent, String resolvedType, Bundle bOptions) {
+        checkCaller();
+
+        int callingUser = UserHandle.getCallingUserId();
+        TaskRecord tr;
+        IApplicationThread appThread;
+        synchronized (mService) {
+            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+            if (tr == null) {
+                throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+            }
+            appThread = IApplicationThread.Stub.asInterface(whoThread);
+            if (appThread == null) {
+                throw new IllegalArgumentException("Bad app thread " + appThread);
+            }
+        }
+        return mService.mActivityStarter.startActivityMayWait(appThread, -1, callingPackage,
+                intent, resolvedType, null, null, null, null, 0, 0, null, null,
+                null, bOptions, false, callingUser, tr, "AppTaskImpl");
+    }
+
+    @Override
+    public void setExcludeFromRecents(boolean exclude) {
+        checkCaller();
+
+        synchronized (mService) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+                if (tr == null) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+                Intent intent = tr.getBaseIntent();
+                if (exclude) {
+                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                } else {
+                    intent.setFlags(intent.getFlags()
+                            & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 5c48f90..ba541e6 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.am;
 
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index d652341..0dc73e9 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -24,8 +24,8 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Slog;
-import android.view.Display;
 import android.view.Gravity;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 
@@ -64,43 +64,11 @@
     private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2;
     private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3;
 
-    private boolean mDefaultStartBoundsConfigurationSet = false;
     private final Rect mAvailableRect = new Rect();
     private final Rect mTmpProposal = new Rect();
     private final Rect mTmpOriginal = new Rect();
 
-    private int mDefaultFreeformStartX;
-    private int mDefaultFreeformStartY;
-    private int mDefaultFreeformWidth;
-    private int mDefaultFreeformHeight;
-    private int mDefaultFreeformStepHorizontal;
-    private int mDefaultFreeformStepVertical;
-    private int mDisplayWidth;
-    private int mDisplayHeight;
-
-    void setDisplay(Display display) {
-        Point size = new Point();
-        display.getSize(size);
-        mDisplayWidth = size.x;
-        mDisplayHeight = size.y;
-    }
-
-    void configure(Rect stackBounds) {
-        if (stackBounds == null) {
-            mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight);
-        } else {
-            mAvailableRect.set(stackBounds);
-        }
-        int width = mAvailableRect.width();
-        int height = mAvailableRect.height();
-        mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR;
-        mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR;
-        mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR;
-        mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR;
-        mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP);
-        mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP);
-        mDefaultStartBoundsConfigurationSet = true;
-    }
+    private final Point mDisplaySize = new Point();
 
     /**
      * Tries to set task's bound in a way that it won't collide with any other task. By colliding
@@ -114,104 +82,154 @@
      */
     void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
             @Nullable ActivityInfo.WindowLayout windowLayout) {
-        if (!mDefaultStartBoundsConfigurationSet) {
-            return;
-        }
+        updateAvailableRect(task, mAvailableRect);
+
         if (windowLayout == null) {
-            positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight);
+            positionCenter(task, tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
+                    getFreeformHeight(mAvailableRect));
             return;
         }
-        int width = getFinalWidth(windowLayout);
-        int height = getFinalHeight(windowLayout);
+        int width = getFinalWidth(windowLayout, mAvailableRect);
+        int height = getFinalHeight(windowLayout, mAvailableRect);
         int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
         int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
         if (verticalGravity == Gravity.TOP) {
             if (horizontalGravity == Gravity.RIGHT) {
-                positionTopRight(task, tasks, width, height);
+                positionTopRight(task, tasks, mAvailableRect, width, height);
             } else {
-                positionTopLeft(task, tasks, width, height);
+                positionTopLeft(task, tasks, mAvailableRect, width, height);
             }
         } else if (verticalGravity == Gravity.BOTTOM) {
             if (horizontalGravity == Gravity.RIGHT) {
-                positionBottomRight(task, tasks, width, height);
+                positionBottomRight(task, tasks, mAvailableRect, width, height);
             } else {
-                positionBottomLeft(task, tasks, width, height);
+                positionBottomLeft(task, tasks, mAvailableRect, width, height);
             }
         } else {
             // Some fancy gravity setting that we don't support yet. We just put the activity in the
             // center.
             Slog.w(TAG, "Received unsupported gravity: " + windowLayout.gravity
                     + ", positioning in the center instead.");
-            positionCenter(task, tasks, width, height);
+            positionCenter(task, tasks, mAvailableRect, width, height);
         }
     }
 
-    private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) {
-        int width = mDefaultFreeformWidth;
+    private void updateAvailableRect(TaskRecord task, Rect availableRect) {
+        final Rect stackBounds = task.getStack().mBounds;
+
+        if (stackBounds != null) {
+            availableRect.set(stackBounds);
+        } else {
+            task.getStack().getDisplay().mDisplay.getSize(mDisplaySize);
+            availableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+        }
+    }
+
+    @VisibleForTesting
+    static int getFreeformStartLeft(Rect bounds) {
+        return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR;
+    }
+
+    @VisibleForTesting
+    static int getFreeformStartTop(Rect bounds) {
+        return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR;
+    }
+
+    @VisibleForTesting
+    static int getFreeformWidth(Rect bounds) {
+        return bounds.width() / WINDOW_SIZE_DENOMINATOR;
+    }
+
+    @VisibleForTesting
+    static int getFreeformHeight(Rect bounds) {
+        return bounds.height() / WINDOW_SIZE_DENOMINATOR;
+    }
+
+    @VisibleForTesting
+    static int getHorizontalStep(Rect bounds) {
+        return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP);
+    }
+
+    @VisibleForTesting
+    static int getVerticalStep(Rect bounds) {
+        return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP);
+    }
+
+
+
+    private int getFinalWidth(ActivityInfo.WindowLayout windowLayout, Rect availableRect) {
+        int width = getFreeformWidth(availableRect);
         if (windowLayout.width > 0) {
             width = windowLayout.width;
         }
         if (windowLayout.widthFraction > 0) {
-            width = (int) (mAvailableRect.width() * windowLayout.widthFraction);
+            width = (int) (availableRect.width() * windowLayout.widthFraction);
         }
         return width;
     }
 
-    private int getFinalHeight(ActivityInfo.WindowLayout windowLayout) {
-        int height = mDefaultFreeformHeight;
+    private int getFinalHeight(ActivityInfo.WindowLayout windowLayout, Rect availableRect) {
+        int height = getFreeformHeight(availableRect);
         if (windowLayout.height > 0) {
             height = windowLayout.height;
         }
         if (windowLayout.heightFraction > 0) {
-            height = (int) (mAvailableRect.height() * windowLayout.heightFraction);
+            height = (int) (availableRect.height() * windowLayout.heightFraction);
         }
         return height;
     }
 
-    private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
-            int height) {
-        mTmpProposal.set(mAvailableRect.left, mAvailableRect.bottom - height,
-                mAvailableRect.left + width, mAvailableRect.bottom);
-        position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
+    private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
+            Rect availableRect, int width, int height) {
+        mTmpProposal.set(availableRect.left, availableRect.bottom - height,
+                availableRect.left + width, availableRect.bottom);
+        position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
+                SHIFT_POLICY_HORIZONTAL_RIGHT);
     }
 
-    private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
-            int height) {
-        mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.bottom - height,
-                mAvailableRect.right, mAvailableRect.bottom);
-        position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
+    private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks,
+            Rect availableRect, int width, int height) {
+        mTmpProposal.set(availableRect.right - width, availableRect.bottom - height,
+                availableRect.right, availableRect.bottom);
+        position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
+                SHIFT_POLICY_HORIZONTAL_LEFT);
     }
 
-    private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
-            int height) {
-        mTmpProposal.set(mAvailableRect.left, mAvailableRect.top,
-                mAvailableRect.left + width, mAvailableRect.top + height);
-        position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
+    private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
+            Rect availableRect, int width, int height) {
+        mTmpProposal.set(availableRect.left, availableRect.top,
+                availableRect.left + width, availableRect.top + height);
+        position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
+                SHIFT_POLICY_HORIZONTAL_RIGHT);
     }
 
-    private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
-            int height) {
-        mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.top,
-                mAvailableRect.right, mAvailableRect.top + height);
-        position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
+    private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks,
+            Rect availableRect, int width, int height) {
+        mTmpProposal.set(availableRect.right - width, availableRect.top,
+                availableRect.right, availableRect.top + height);
+        position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
+                SHIFT_POLICY_HORIZONTAL_LEFT);
     }
 
-    private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
-            int height) {
-        mTmpProposal.set(mDefaultFreeformStartX, mDefaultFreeformStartY,
-                mDefaultFreeformStartX + width, mDefaultFreeformStartY + height);
-        position(task, tasks, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN);
+    private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks,
+            Rect availableRect, int width, int height) {
+        final int defaultFreeformLeft = getFreeformStartLeft(availableRect);
+        final int defaultFreeformTop = getFreeformStartTop(availableRect);
+        mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop,
+                defaultFreeformLeft + width, defaultFreeformTop + height);
+        position(task, tasks, availableRect, mTmpProposal, ALLOW_RESTART,
+                SHIFT_POLICY_DIAGONAL_DOWN);
     }
 
-    private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect proposal,
-            boolean allowRestart, int shiftPolicy) {
+    private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect availableRect,
+            Rect proposal, boolean allowRestart, int shiftPolicy) {
         mTmpOriginal.set(proposal);
         boolean restarted = false;
         while (boundsConflict(proposal, tasks)) {
             // Unfortunately there is already a task at that spot, so we need to look for some
             // other place.
-            shiftStartingPoint(proposal, shiftPolicy);
-            if (shiftedToFar(proposal, shiftPolicy)) {
+            shiftStartingPoint(proposal, availableRect, shiftPolicy);
+            if (shiftedTooFar(proposal, availableRect, shiftPolicy)) {
                 // We don't want the task to go outside of the stack, because it won't look
                 // nice. Depending on the starting point we either restart, or immediately give up.
                 if (!allowRestart) {
@@ -220,13 +238,13 @@
                 }
                 // We must have started not from the top. Let's restart from there because there
                 // might be some space there.
-                proposal.set(mAvailableRect.left, mAvailableRect.top,
-                        mAvailableRect.left + proposal.width(),
-                        mAvailableRect.top + proposal.height());
+                proposal.set(availableRect.left, availableRect.top,
+                        availableRect.left + proposal.width(),
+                        availableRect.top + proposal.height());
                 restarted = true;
             }
-            if (restarted && (proposal.left > mDefaultFreeformStartX
-                    || proposal.top > mDefaultFreeformStartY)) {
+            if (restarted && (proposal.left > getFreeformStartLeft(availableRect)
+                    || proposal.top > getFreeformStartTop(availableRect))) {
                 // If we restarted and crossed the initial position, let's not struggle anymore.
                 // The user already must have ton of tasks visible, we can just smack the new
                 // one in the center.
@@ -237,27 +255,30 @@
         task.updateOverrideConfiguration(proposal);
     }
 
-    private boolean shiftedToFar(Rect start, int shiftPolicy) {
+    private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) {
         switch (shiftPolicy) {
             case SHIFT_POLICY_HORIZONTAL_LEFT:
-                return start.left < mAvailableRect.left;
+                return start.left < availableRect.left;
             case SHIFT_POLICY_HORIZONTAL_RIGHT:
-                return start.right > mAvailableRect.right;
+                return start.right > availableRect.right;
             default: // SHIFT_POLICY_DIAGONAL_DOWN
-                return start.right > mAvailableRect.right || start.bottom > mAvailableRect.bottom;
+                return start.right > availableRect.right || start.bottom > availableRect.bottom;
         }
     }
 
-    private void shiftStartingPoint(Rect posposal, int shiftPolicy) {
+    private void shiftStartingPoint(Rect posposal, Rect availableRect, int shiftPolicy) {
+        final int defaultFreeformStepHorizontal = getHorizontalStep(availableRect);
+        final int defaultFreeformStepVertical = getVerticalStep(availableRect);
+
         switch (shiftPolicy) {
             case SHIFT_POLICY_HORIZONTAL_LEFT:
-                posposal.offset(-mDefaultFreeformStepHorizontal, 0);
+                posposal.offset(-defaultFreeformStepHorizontal, 0);
                 break;
             case SHIFT_POLICY_HORIZONTAL_RIGHT:
-                posposal.offset(mDefaultFreeformStepHorizontal, 0);
+                posposal.offset(defaultFreeformStepHorizontal, 0);
                 break;
             default: // SHIFT_POLICY_DIAGONAL_DOWN:
-                posposal.offset(mDefaultFreeformStepHorizontal, mDefaultFreeformStepVertical);
+                posposal.offset(defaultFreeformStepHorizontal, defaultFreeformStepVertical);
                 break;
         }
     }
@@ -296,8 +317,4 @@
         return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE
                 && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE;
     }
-
-    void reset() {
-        mDefaultStartBoundsConfigurationSet = false;
-    }
 }
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 365c5b1..8fef6be 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -16,15 +16,25 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
 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.REMOVE_FROM_RECENTS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import com.google.android.collect.Sets;
@@ -37,43 +47,87 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Environment;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.MutableBoolean;
+import android.util.MutableInt;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.ActivityStack.ActivityState;
+
 import java.io.File;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
- * Class for managing the recent tasks list.
+ * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
+ * least recent.
  */
-class RecentTasks extends ArrayList<TaskRecord> {
+class RecentTasks {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final boolean TRIMMED = true;
 
-    // Maximum number recent bitmaps to keep in memory.
-    private static final int MAX_RECENT_BITMAPS = 3;
     private static final int DEFAULT_INITIAL_CAPACITY = 5;
 
     // Whether or not to move all affiliated tasks to the front when one of the tasks is launched
     private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
 
+    // Comparator to sort by taskId
+    private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
+            (lhs, rhs) -> rhs.taskId - lhs.taskId;
+
+    // Placeholder variables to keep track of activities/apps that are no longer avialble while
+    // iterating through the recents list
+    private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
+    private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
+
+    /**
+     * Callbacks made when manipulating the list.
+     */
+    interface Callbacks {
+        /**
+         * Called when a task is added to the recent tasks list.
+         */
+        void onRecentTaskAdded(TaskRecord task);
+
+        /**
+         * Called when a task is removed from the recent tasks list.
+         */
+        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
+    }
+
     /**
      * Save recent tasks information across reboots.
      */
     private final TaskPersister mTaskPersister;
     private final ActivityManagerService mService;
+    private final UserController mUserController;
+
+    /**
+     * Mapping of user id -> whether recent tasks have been loaded for that user.
+     */
     private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
             DEFAULT_INITIAL_CAPACITY);
 
@@ -81,21 +135,106 @@
      * Stores for each user task ids that are taken by tasks residing in persistent storage. These
      * tasks may or may not currently be in memory.
      */
-    final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
+    private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
             DEFAULT_INITIAL_CAPACITY);
 
+    // List of all active recent tasks
+    private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
+    private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
+
+    // These values are generally loaded from resources, but can be set dynamically in the tests
+    private boolean mHasVisibleRecentTasks;
+    private int mGlobalMaxNumTasks;
+    private int mMinNumVisibleTasks;
+    private int mMaxNumVisibleTasks;
+    private long mActiveTasksSessionDurationMs;
+
     // Mainly to avoid object recreation on multiple calls.
-    private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>();
+    private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
     private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
     private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
-    private final ActivityInfo mTmpActivityInfo = new ActivityInfo();
-    private final ApplicationInfo mTmpAppInfo = new ApplicationInfo();
+    private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
 
-    RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
-        File systemDir = Environment.getDataSystemDirectory();
+    @VisibleForTesting
+    RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
+            UserController userController) {
         mService = service;
-        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
-        mStackSupervisor.setRecentTasks(this);
+        mUserController = userController;
+        mTaskPersister = taskPersister;
+        mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
+        mHasVisibleRecentTasks = true;
+    }
+
+    RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) {
+        final File systemDir = Environment.getDataSystemDirectory();
+        final Resources res = service.mContext.getResources();
+        mService = service;
+        mUserController = service.mUserController;
+        mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
+        mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
+        mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
+        loadParametersFromResources(service.mContext.getResources());
+    }
+
+    @VisibleForTesting
+    void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
+            long activeSessionDurationMs) {
+        mMinNumVisibleTasks = minNumVisibleTasks;
+        mMaxNumVisibleTasks = maxNumVisibleTasks;
+        mActiveTasksSessionDurationMs = activeSessionDurationMs;
+    }
+
+    @VisibleForTesting
+    void setGlobalMaxNumTasks(int globalMaxNumTasks) {
+        mGlobalMaxNumTasks = globalMaxNumTasks;
+    }
+
+    /**
+     * Loads the parameters from the system resources.
+     */
+    @VisibleForTesting
+    void loadParametersFromResources(Resources res) {
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
+        } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
+        } else {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
+        }
+        final int sessionDurationHrs = res.getInteger(
+                com.android.internal.R.integer.config_activeTaskDurationHours);
+        mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
+                ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
+                : -1;
+    }
+
+    void registerCallback(Callbacks callback) {
+        mCallbacks.add(callback);
+    }
+
+    void unregisterCallback(Callbacks callback) {
+        mCallbacks.remove(callback);
+    }
+
+    private void notifyTaskAdded(TaskRecord task) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onRecentTaskAdded(task);
+        }
+    }
+
+    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
+        }
     }
 
     /**
@@ -106,6 +245,7 @@
      */
     void loadUserRecentsLocked(int userId) {
         if (mUsersWithRecentsLoaded.get(userId)) {
+            // User already loaded, return early
             return;
         }
 
@@ -114,14 +254,14 @@
 
         // Check if any tasks are added before recents is loaded
         final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
-        for (final TaskRecord task : this) {
+        for (final TaskRecord task : mTasks) {
             if (task.userId == userId && shouldPersistTaskLocked(task)) {
                 preaddedTasks.put(task.taskId, true);
             }
         }
 
         Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
-        addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
+        mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
         cleanupLocked(userId);
         mUsersWithRecentsLoaded.put(userId, true);
 
@@ -140,11 +280,25 @@
         }
     }
 
-    boolean taskIdTakenForUserLocked(int taskId, int userId) {
+    /**
+     * @return whether the {@param taskId} is currently in use for the given user.
+     */
+    boolean containsTaskId(int taskId, int userId) {
         loadPersistedTaskIdsForUserLocked(userId);
         return mPersistedTaskIds.get(userId).get(taskId);
     }
 
+    /**
+     * @return all the task ids for the user with the given {@param userId}.
+     */
+    SparseBooleanArray getTaskIdsForUser(int userId) {
+        loadPersistedTaskIdsForUserLocked(userId);
+        return mPersistedTaskIds.get(userId);
+    }
+
+    /**
+     * Kicks off the task persister to write any pending tasks to disk.
+     */
     void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
         final ActivityStack stack = task != null ? task.getStack() : null;
         if (stack != null && stack.isHomeOrRecentsStack()) {
@@ -164,8 +318,8 @@
                 mPersistedTaskIds.valueAt(i).clear();
             }
         }
-        for (int i = size() - 1; i >= 0; i--) {
-            final TaskRecord task = get(i);
+        for (int i = mTasks.size() - 1; i >= 0; i--) {
+            final TaskRecord task = mTasks.get(i);
             if (shouldPersistTaskLocked(task)) {
                 // Set of persisted taskIds for task.userId should not be null here
                 // TODO Investigate why it can happen. For now initialize with an empty set
@@ -180,12 +334,12 @@
     }
 
     private static boolean shouldPersistTaskLocked(TaskRecord task) {
-        final ActivityStack<?> stack = task.getStack();
+        final ActivityStack stack = task.getStack();
         return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
     }
 
     void onSystemReadyLocked() {
-        clear();
+        mTasks.clear();
         mTaskPersister.startPersisting();
     }
 
@@ -225,14 +379,6 @@
         return usersWithRecentsLoaded;
     }
 
-    private void unloadUserRecentsLocked(int userId) {
-        if (mUsersWithRecentsLoaded.get(userId)) {
-            Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
-            mUsersWithRecentsLoaded.delete(userId);
-            removeTasksForUserLocked(userId);
-        }
-    }
-
     /**
      * Removes recent tasks and any other state kept in memory for the passed in user. Does not
      * touch the information present on persistent storage.
@@ -240,44 +386,36 @@
      * @param userId the id of the user
      */
     void unloadUserDataFromMemoryLocked(int userId) {
-        unloadUserRecentsLocked(userId);
+        if (mUsersWithRecentsLoaded.get(userId)) {
+            Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
+            mUsersWithRecentsLoaded.delete(userId);
+            removeTasksForUserLocked(userId);
+        }
         mPersistedTaskIds.delete(userId);
         mTaskPersister.unloadUserDataFromMemory(userId);
     }
 
-    TaskRecord taskForIdLocked(int id) {
-        final int recentsCount = size();
-        for (int i = 0; i < recentsCount; i++) {
-            TaskRecord tr = get(i);
-            if (tr.taskId == id) {
-                return tr;
-            }
-        }
-        return null;
-    }
-
     /** Remove recent tasks for a user. */
-    void removeTasksForUserLocked(int userId) {
+    private void removeTasksForUserLocked(int userId) {
         if(userId <= 0) {
             Slog.i(TAG, "Can't remove recent task on user " + userId);
             return;
         }
 
-        for (int i = size() - 1; i >= 0; --i) {
-            TaskRecord tr = get(i);
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            TaskRecord tr = mTasks.get(i);
             if (tr.userId == userId) {
                 if(DEBUG_TASKS) Slog.i(TAG_TASKS,
                         "remove RecentTask " + tr + " when finishing user" + userId);
-                remove(i);
-                tr.removedFromRecents();
+                remove(mTasks.get(i));
             }
         }
     }
 
     void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
         final Set<String> packageNames = Sets.newHashSet(packages);
-        for (int i = size() - 1; i >= 0; --i) {
-            final TaskRecord tr = get(i);
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
             if (tr.realActivity != null
                     && packageNames.contains(tr.realActivity.getPackageName())
                     && tr.userId == userId
@@ -286,7 +424,38 @@
                notifyTaskPersisterLocked(tr, false);
             }
         }
+    }
 
+    void removeTasksByPackageName(String packageName, int userId) {
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            final String taskPackageName =
+                    tr.getBaseIntent().getComponent().getPackageName();
+            if (tr.userId != userId) return;
+            if (!taskPackageName.equals(packageName)) return;
+
+            mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+        }
+    }
+
+    void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
+            int userId) {
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            if (userId != UserHandle.USER_ALL && tr.userId != userId) {
+                continue;
+            }
+
+            ComponentName cn = tr.intent.getComponent();
+            final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
+                    && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
+            if (sameComponent) {
+                mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                        REMOVE_FROM_RECENTS);
+            }
+        }
     }
 
     /**
@@ -295,24 +464,28 @@
      * of affiliations.
      */
     void cleanupLocked(int userId) {
-        int recentsCount = size();
+        int recentsCount = mTasks.size();
         if (recentsCount == 0) {
             // Happens when called from the packagemanager broadcast before boot,
             // or just any empty list.
             return;
         }
 
+        // Clear the temp lists
+        mTmpAvailActCache.clear();
+        mTmpAvailAppCache.clear();
+
         final IPackageManager pm = AppGlobals.getPackageManager();
         for (int i = recentsCount - 1; i >= 0; i--) {
-            final TaskRecord task = get(i);
+            final TaskRecord task = mTasks.get(i);
             if (userId != UserHandle.USER_ALL && task.userId != userId) {
                 // Only look at tasks for the user ID of interest.
                 continue;
             }
             if (task.autoRemoveRecents && task.getTopActivity() == null) {
                 // This situation is broken, and we should just get rid of it now.
-                remove(i);
-                task.removedFromRecents();
+                mTasks.remove(i);
+                notifyTaskRemoved(task, !TRIMMED);
                 Slog.w(TAG, "Removing auto-remove without activity: " + task);
                 continue;
             }
@@ -331,11 +504,11 @@
                         continue;
                     }
                     if (ai == null) {
-                        ai = mTmpActivityInfo;
+                        ai = NO_ACTIVITY_INFO_TOKEN;
                     }
                     mTmpAvailActCache.put(task.realActivity, ai);
                 }
-                if (ai == mTmpActivityInfo) {
+                if (ai == NO_ACTIVITY_INFO_TOKEN) {
                     // This could be either because the activity no longer exists, or the
                     // app is temporarily gone. For the former we want to remove the recents
                     // entry; for the latter we want to mark it as unavailable.
@@ -350,15 +523,15 @@
                             continue;
                         }
                         if (app == null) {
-                            app = mTmpAppInfo;
+                            app = NO_APPLICATION_INFO_TOKEN;
                         }
                         mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
                     }
-                    if (app == mTmpAppInfo
+                    if (app == NO_APPLICATION_INFO_TOKEN
                             || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
                         // Doesn't exist any more! Good-bye.
-                        remove(i);
-                        task.removedFromRecents();
+                        mTasks.remove(i);
+                        notifyTaskRemoved(task, !TRIMMED);
                         Slog.w(TAG, "Removing no longer valid recent: " + task);
                         continue;
                     } else {
@@ -390,15 +563,670 @@
 
         // Verify the affiliate chain for each task.
         int i = 0;
-        recentsCount = size();
+        recentsCount = mTasks.size();
         while (i < recentsCount) {
             i = processNextAffiliateChainLocked(i);
         }
         // recent tasks are now in sorted, affiliated order.
     }
 
-    private final boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
-        int recentsCount = size();
+    /**
+     * @return whether the given {@param task} can be added to the list without causing another
+     * task to be trimmed as a result of that add.
+     */
+    private boolean canAddTaskWithoutTrim(TaskRecord task) {
+        return findTrimIndexForAddTask(task) == -1;
+    }
+
+    /**
+     * Returns the list of {@link ActivityManager.AppTask}s.
+     */
+    ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
+        final ArrayList<IBinder> list = new ArrayList<>();
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            // Skip tasks that do not match the caller.  We don't need to verify
+            // callingPackage, because we are also limiting to callingUid and know
+            // that will limit to the correct security sandbox.
+            if (tr.effectiveUid != callingUid) {
+                continue;
+            }
+            Intent intent = tr.getBaseIntent();
+            if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
+                continue;
+            }
+            ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr);
+            AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid);
+            list.add(taskImpl.asBinder());
+        }
+        return list;
+    }
+
+    /**
+     * @return the list of recent tasks for presentation.
+     */
+    ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
+        final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
+
+        if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
+            Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
+            return ParceledListSlice.emptyList();
+        }
+        loadUserRecentsLocked(userId);
+
+        final Set<Integer> includedUsers = mUserController.getProfileIds(userId);
+        includedUsers.add(Integer.valueOf(userId));
+
+        final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
+        final int size = mTasks.size();
+        int numVisibleTasks = 0;
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+
+            if (isVisibleRecentTask(tr)) {
+                numVisibleTasks++;
+                if (isInVisibleRange(tr, numVisibleTasks)) {
+                    // Fall through
+                } else {
+                    // Not in visible range
+                    continue;
+                }
+            } else {
+                // Not visible
+                continue;
+            }
+
+            // Skip remaining tasks once we reach the requested size
+            if (res.size() >= maxNum) {
+                continue;
+            }
+
+            // Only add calling user or related users recent tasks
+            if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
+                continue;
+            }
+
+            if (tr.realActivitySuspended) {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
+                continue;
+            }
+
+            // Return the entry if desired by the caller.  We always return
+            // the first entry, because callers always expect this to be the
+            // foreground app.  We may filter others if the caller has
+            // not supplied RECENT_WITH_EXCLUDED and there is some reason
+            // we should exclude the entry.
+
+            if (i == 0
+                    || withExcluded
+                    || (tr.intent == null)
+                    || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    == 0)) {
+                if (!getTasksAllowed) {
+                    // If the caller doesn't have the GET_TASKS permission, then only
+                    // allow them to see a small subset of tasks -- their own and home.
+                    if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
+                        continue;
+                    }
+                }
+                if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
+                    // Don't include auto remove tasks that are finished or finishing.
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, auto-remove without activity: " + tr);
+                    continue;
+                }
+                if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, unavail real act: " + tr);
+                    continue;
+                }
+
+                if (!tr.mUserSetupComplete) {
+                    // Don't include task launched while user is not done setting-up.
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, user setup not complete: " + tr);
+                    continue;
+                }
+
+                ActivityManager.RecentTaskInfo rti = RecentTasks.createRecentTaskInfo(tr);
+                if (!getDetailedTasks) {
+                    rti.baseIntent.replaceExtras((Bundle)null);
+                }
+
+                res.add(rti);
+            }
+        }
+        return new ParceledListSlice<>(res);
+    }
+
+    /**
+     * @return the list of persistable task ids.
+     */
+    void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord task = mTasks.get(i);
+            if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
+                    + " persistable=" + task.isPersistable);
+            final ActivityStack stack = task.getStack();
+            if ((task.isPersistable || task.inRecents)
+                    && (stack == null || !stack.isHomeOrRecentsStack())) {
+                if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+                persistentTaskIds.add(task.taskId);
+            } else {
+                if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
+                        + task);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    ArrayList<TaskRecord> getRawTasks() {
+        return mTasks;
+    }
+
+    /**
+     * @return the task in the task list with the given {@param id} if one exists.
+     */
+    TaskRecord getTask(int id) {
+        final int recentsCount = mTasks.size();
+        for (int i = 0; i < recentsCount; i++) {
+            TaskRecord tr = mTasks.get(i);
+            if (tr.taskId == id) {
+                return tr;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Add a new task to the recent tasks list.
+     */
+    void add(TaskRecord task) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
+
+        final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
+                || task.mNextAffiliateTaskId != INVALID_TASK_ID
+                || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
+
+        int recentsCount = mTasks.size();
+        // Quick case: never add voice sessions.
+        // TODO: VI what about if it's just an activity?
+        // Probably nothing to do here
+        if (task.voiceSession != null) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                    "addRecent: not adding voice interaction " + task);
+            return;
+        }
+        // Another quick case: check if the top-most recent task is the same.
+        if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
+            return;
+        }
+        // Another quick case: check if this is part of a set of affiliated
+        // tasks that are at the top.
+        if (isAffiliated && recentsCount > 0 && task.inRecents
+                && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
+                    + " at top when adding " + task);
+            return;
+        }
+
+        boolean needAffiliationFix = false;
+
+        // Slightly less quick case: the task is already in recents, so all we need
+        // to do is move it.
+        if (task.inRecents) {
+            int taskIndex = mTasks.indexOf(task);
+            if (taskIndex >= 0) {
+                if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
+                    // Simple case: this is not an affiliated task, so we just move it to the front.
+                    mTasks.remove(taskIndex);
+                    mTasks.add(0, task);
+                    notifyTaskPersisterLocked(task, false);
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
+                            + " from " + taskIndex);
+                    return;
+                } else {
+                    // More complicated: need to keep all affiliated tasks together.
+                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
+                        // All went well.
+                        return;
+                    }
+
+                    // Uh oh...  something bad in the affiliation chain, try to rebuild
+                    // everything and then go through our general path of adding a new task.
+                    needAffiliationFix = true;
+                }
+            } else {
+                Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
+                needAffiliationFix = true;
+            }
+        }
+
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
+        trimForAddTask(task);
+
+        task.inRecents = true;
+        if (!isAffiliated || needAffiliationFix) {
+            // If this is a simple non-affiliated task, or we had some failure trying to
+            // handle it as part of an affilated task, then just place it at the top.
+            mTasks.add(0, task);
+            notifyTaskAdded(task);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
+        } else if (isAffiliated) {
+            // If this is a new affiliated task, then move all of the affiliated tasks
+            // to the front and insert this new one.
+            TaskRecord other = task.mNextAffiliate;
+            if (other == null) {
+                other = task.mPrevAffiliate;
+            }
+            if (other != null) {
+                int otherIndex = mTasks.indexOf(other);
+                if (otherIndex >= 0) {
+                    // Insert new task at appropriate location.
+                    int taskIndex;
+                    if (other == task.mNextAffiliate) {
+                        // We found the index of our next affiliation, which is who is
+                        // before us in the list, so add after that point.
+                        taskIndex = otherIndex+1;
+                    } else {
+                        // We found the index of our previous affiliation, which is who is
+                        // after us in the list, so add at their position.
+                        taskIndex = otherIndex;
+                    }
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: new affiliated task added at " + taskIndex + ": " + task);
+                    mTasks.add(taskIndex, task);
+                    notifyTaskAdded(task);
+
+                    // Now move everything to the front.
+                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
+                        // All went well.
+                        return;
+                    }
+
+                    // Uh oh...  something bad in the affiliation chain, try to rebuild
+                    // everything and then go through our general path of adding a new task.
+                    needAffiliationFix = true;
+                } else {
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: couldn't find other affiliation " + other);
+                    needAffiliationFix = true;
+                }
+            } else {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                        "addRecent: adding affiliated task without next/prev:" + task);
+                needAffiliationFix = true;
+            }
+        }
+
+        if (needAffiliationFix) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
+            cleanupLocked(task.userId);
+        }
+
+        // Trim the set of tasks to the active set
+        trimInactiveRecentTasks();
+    }
+
+    /**
+     * Add the task to the bottom if possible.
+     */
+    boolean addToBottom(TaskRecord task) {
+        if (!canAddTaskWithoutTrim(task)) {
+            // Adding this task would cause the task to be removed (since it's appended at
+            // the bottom and would be trimmed) so just return now
+            return false;
+        }
+
+        add(task);
+        return true;
+    }
+
+    /**
+     * Remove a task from the recent tasks list.
+     */
+    void remove(TaskRecord task) {
+        mTasks.remove(task);
+        notifyTaskRemoved(task, !TRIMMED);
+    }
+
+    /**
+     * Trims the recents task list to the global max number of recents.
+     */
+    private void trimInactiveRecentTasks() {
+        int recentsCount = mTasks.size();
+
+        // Remove from the end of the list until we reach the max number of recents
+        while (recentsCount > mGlobalMaxNumTasks) {
+            final TaskRecord tr = mTasks.remove(recentsCount - 1);
+            notifyTaskRemoved(tr, TRIMMED);
+            recentsCount--;
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
+                    + " max=" + mGlobalMaxNumTasks);
+        }
+
+        // Remove any tasks that belong to currently quiet profiles
+        final int[] profileUserIds = mUserController.getCurrentProfileIds();
+        mTmpQuietProfileUserIds.clear();
+        for (int userId : profileUserIds) {
+            final UserInfo userInfo = mUserController.getUserInfo(userId);
+            if (userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
+                mTmpQuietProfileUserIds.put(userId, true);
+            }
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
+                    + " quiet=" + mTmpQuietProfileUserIds.get(userId));
+        }
+
+        // Remove any inactive tasks, calculate the latest set of visible tasks
+        int numVisibleTasks = 0;
+        for (int i = 0; i < mTasks.size();) {
+            final TaskRecord task = mTasks.get(i);
+
+            if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
+                if (!mHasVisibleRecentTasks) {
+                    // Keep all active tasks if visible recent tasks is not supported
+                    i++;
+                    continue;
+                }
+
+                if (!isVisibleRecentTask(task)) {
+                    // Keep all active-but-invisible tasks
+                    i++;
+                    continue;
+                } else {
+                    numVisibleTasks++;
+                    if (isInVisibleRange(task, numVisibleTasks)) {
+                        // Keep visible tasks in range
+                        i++;
+                        continue;
+                    } else {
+                        // Fall through to trim visible tasks that are no longer in range
+                        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
+                                "Trimming out-of-range visible task=" + task);
+                    }
+                }
+            } else {
+                // Fall through to trim inactive tasks
+                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
+            }
+
+            // Task is no longer active, trim it from the list
+            mTasks.remove(task);
+            notifyTaskRemoved(task, TRIMMED);
+            notifyTaskPersisterLocked(task, false /* flush */);
+        }
+    }
+
+    /**
+     * @return whether the given task should be considered active.
+     */
+    private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
+                + " globalMax=" + mGlobalMaxNumTasks);
+
+        if (quietProfileUserIds.get(task.userId)) {
+            // Quiet profile user's tasks are never active
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
+            return false;
+        }
+
+        if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
+            // Keep the task active if its affiliated task is also active
+            final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
+            if (affiliatedTask != null) {
+                if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
+                    if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
+                            "\taffiliatedWithTask=" + affiliatedTask + " is not active");
+                    return false;
+                }
+            }
+        }
+
+        // All other tasks are considered active
+        return true;
+    }
+
+    /**
+     * @return whether the given active task should be presented to the user through SystemUI.
+     */
+    private boolean isVisibleRecentTask(TaskRecord task) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
+                + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
+                + " sessionDuration=" + mActiveTasksSessionDurationMs
+                + " inactiveDuration=" + task.getInactiveDuration()
+                + " activityType=" + task.getActivityType()
+                + " windowingMode=" + task.getWindowingMode());
+
+        // Ignore certain activity types completely
+        switch (task.getActivityType()) {
+            case ACTIVITY_TYPE_HOME:
+            case ACTIVITY_TYPE_RECENTS:
+                return false;
+        }
+
+        // Ignore certain windowing modes
+        switch (task.getWindowingMode()) {
+            case WINDOWING_MODE_PINNED:
+                return false;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
+                final ActivityStack stack = task.getStack();
+                if (stack != null && stack.topTask() == task) {
+                    // Only the non-top task of the primary split screen mode is visible
+                    return false;
+                }
+        }
+
+        return true;
+    }
+
+    /**
+     * @return whether the given visible task is within the policy range.
+     */
+    private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
+        // Keep the last most task even if it is excluded from recents
+        final boolean isExcludeFromRecents =
+                (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+        if (isExcludeFromRecents) {
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
+            return numVisibleTasks == 1;
+        }
+
+        if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
+            // Always keep up to the min number of recent tasks, after that fall through to the
+            // checks below
+            return true;
+        }
+
+        if (mMaxNumVisibleTasks >= 0) {
+            // Always keep up to the max number of recent tasks, but return false afterwards
+            return numVisibleTasks <= mMaxNumVisibleTasks;
+        }
+
+        if (mActiveTasksSessionDurationMs > 0) {
+            // Keep the task if the inactive time is within the session window, this check must come
+            // after the checks for the min/max visible task range
+            if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * If needed, remove oldest existing entries in recents that are for the same kind
+     * of task as the given one.
+     */
+    private void trimForAddTask(TaskRecord task) {
+        final int removeIndex = findTrimIndexForAddTask(task);
+        if (removeIndex == -1) {
+            // Nothing to trim
+            return;
+        }
+
+        // There is a similar task that will be removed for the addition of {@param task}, but it
+        // can be the same task, and if so, the task will be re-added in add(), so skip the
+        // callbacks here.
+        final TaskRecord removedTask = mTasks.remove(removeIndex);
+        if (removedTask != task) {
+            notifyTaskRemoved(removedTask, TRIMMED);
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
+                    + " for addition of task=" + task);
+        }
+        notifyTaskPersisterLocked(removedTask, false /* flush */);
+    }
+
+    /**
+     * Find the task that would be removed if the given {@param task} is added to the recent tasks
+     * list (if any).
+     */
+    private int findTrimIndexForAddTask(TaskRecord task) {
+        int recentsCount = mTasks.size();
+        final Intent intent = task.intent;
+        final boolean document = intent != null && intent.isDocument();
+        int maxRecents = task.maxRecents - 1;
+        final ActivityStack stack = task.getStack();
+        for (int i = 0; i < recentsCount; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            final ActivityStack trStack = tr.getStack();
+
+            if (task != tr) {
+                if (stack != null && trStack != null && stack != trStack) {
+                    continue;
+                }
+                if (task.userId != tr.userId) {
+                    continue;
+                }
+                final Intent trIntent = tr.intent;
+                final boolean sameAffinity =
+                        task.affinity != null && task.affinity.equals(tr.affinity);
+                final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
+                boolean multiTasksAllowed = false;
+                final int flags = intent.getFlags();
+                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
+                        && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                    multiTasksAllowed = true;
+                }
+                final boolean trIsDocument = trIntent != null && trIntent.isDocument();
+                final boolean bothDocuments = document && trIsDocument;
+                if (!sameAffinity && !sameIntent && !bothDocuments) {
+                    continue;
+                }
+
+                if (bothDocuments) {
+                    // Do these documents belong to the same activity?
+                    final boolean sameActivity = task.realActivity != null
+                            && tr.realActivity != null
+                            && task.realActivity.equals(tr.realActivity);
+                    if (!sameActivity) {
+                        // If the document is open in another app or is not the same document, we
+                        // don't need to trim it.
+                        continue;
+                    } else if (maxRecents > 0) {
+                        // Otherwise only trim if we are over our max recents for this task
+                        --maxRecents;
+                        if (!sameIntent || multiTasksAllowed) {
+                            // We don't want to trim if we are not over the max allowed entries and
+                            // the tasks are not of the same intent filter, or multiple entries for
+                            // the task is allowed.
+                            continue;
+                        }
+                    }
+                    // Hit the maximum number of documents for this task. Fall through
+                    // and remove this document from recents.
+                } else if (document || trIsDocument) {
+                    // Only one of these is a document. Not the droid we're looking for.
+                    continue;
+                }
+            }
+            return i;
+        }
+        return -1;
+    }
+
+    // Extract the affiliates of the chain containing recent at index start.
+    private int processNextAffiliateChainLocked(int start) {
+        final TaskRecord startTask = mTasks.get(start);
+        final int affiliateId = startTask.mAffiliatedTaskId;
+
+        // Quick identification of isolated tasks. I.e. those not launched behind.
+        if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
+                startTask.mNextAffiliate == null) {
+            // There is still a slim chance that there are other tasks that point to this task
+            // and that the chain is so messed up that this task no longer points to them but
+            // the gain of this optimization outweighs the risk.
+            startTask.inRecents = true;
+            return start + 1;
+        }
+
+        // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
+        mTmpRecents.clear();
+        for (int i = mTasks.size() - 1; i >= start; --i) {
+            final TaskRecord task = mTasks.get(i);
+            if (task.mAffiliatedTaskId == affiliateId) {
+                mTasks.remove(i);
+                mTmpRecents.add(task);
+            }
+        }
+
+        // Sort them all by taskId. That is the order they were create in and that order will
+        // always be correct.
+        Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
+
+        // Go through and fix up the linked list.
+        // The first one is the end of the chain and has no next.
+        final TaskRecord first = mTmpRecents.get(0);
+        first.inRecents = true;
+        if (first.mNextAffiliate != null) {
+            Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
+            first.setNextAffiliate(null);
+            notifyTaskPersisterLocked(first, false);
+        }
+        // Everything in the middle is doubly linked from next to prev.
+        final int tmpSize = mTmpRecents.size();
+        for (int i = 0; i < tmpSize - 1; ++i) {
+            final TaskRecord next = mTmpRecents.get(i);
+            final TaskRecord prev = mTmpRecents.get(i + 1);
+            if (next.mPrevAffiliate != prev) {
+                Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
+                        " setting prev=" + prev);
+                next.setPrevAffiliate(prev);
+                notifyTaskPersisterLocked(next, false);
+            }
+            if (prev.mNextAffiliate != next) {
+                Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
+                        " setting next=" + next);
+                prev.setNextAffiliate(next);
+                notifyTaskPersisterLocked(prev, false);
+            }
+            prev.inRecents = true;
+        }
+        // The last one is the beginning of the list and has no prev.
+        final TaskRecord last = mTmpRecents.get(tmpSize - 1);
+        if (last.mPrevAffiliate != null) {
+            Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
+            last.setPrevAffiliate(null);
+            notifyTaskPersisterLocked(last, false);
+        }
+
+        // Insert the group back into mTmpTasks at start.
+        mTasks.addAll(start, mTmpRecents);
+        mTmpRecents.clear();
+
+        // Let the caller know where we left off.
+        return start + tmpSize;
+    }
+
+    private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
+        int recentsCount = mTasks.size();
         TaskRecord top = task;
         int topIndex = taskIndex;
         while (top.mNextAffiliate != null && topIndex > 0) {
@@ -412,7 +1240,7 @@
         int endIndex = topIndex;
         TaskRecord prev = top;
         while (endIndex < recentsCount) {
-            TaskRecord cur = get(endIndex);
+            TaskRecord cur = mTasks.get(endIndex);
             if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
                     + endIndex + " " + cur);
             if (cur == top) {
@@ -487,8 +1315,8 @@
             for (int i=topIndex; i<=endIndex; i++) {
                 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
                         + " from " + i + " to " + (i-topIndex));
-                TaskRecord cur = remove(i);
-                add(i - topIndex, cur);
+                TaskRecord cur = mTasks.remove(i);
+                mTasks.add(i - topIndex, cur);
             }
             if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks  " +  topIndex
                     + " to " + endIndex);
@@ -499,301 +1327,87 @@
         return false;
     }
 
-    final void addLocked(TaskRecord task) {
-        final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
-                || task.mNextAffiliateTaskId != INVALID_TASK_ID
-                || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
-
-        int recentsCount = size();
-        // Quick case: never add voice sessions.
-        // TODO: VI what about if it's just an activity?
-        // Probably nothing to do here
-        if (task.voiceSession != null) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                    "addRecent: not adding voice interaction " + task);
-            return;
-        }
-        // Another quick case: check if the top-most recent task is the same.
-        if (!isAffiliated && recentsCount > 0 && get(0) == task) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
-            return;
-        }
-        // Another quick case: check if this is part of a set of affiliated
-        // tasks that are at the top.
-        if (isAffiliated && recentsCount > 0 && task.inRecents
-                && task.mAffiliatedTaskId == get(0).mAffiliatedTaskId) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + get(0)
-                    + " at top when adding " + task);
+    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
+        if (mTasks.isEmpty()) {
             return;
         }
 
-        boolean needAffiliationFix = false;
+        final MutableBoolean printedAnything = new MutableBoolean(false);
+        final MutableBoolean printedHeader = new MutableBoolean(false);
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            if (dumpPackage != null && (tr.realActivity == null ||
+                    !dumpPackage.equals(tr.realActivity.getPackageName()))) {
+                continue;
+            }
 
-        // Slightly less quick case: the task is already in recents, so all we need
-        // to do is move it.
-        if (task.inRecents) {
-            int taskIndex = indexOf(task);
-            if (taskIndex >= 0) {
-                if (!isAffiliated || MOVE_AFFILIATED_TASKS_TO_FRONT) {
-                    // Simple case: this is not an affiliated task, so we just move it to the front.
-                    remove(taskIndex);
-                    add(0, task);
-                    notifyTaskPersisterLocked(task, false);
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
-                            + " from " + taskIndex);
-                    return;
-                } else {
-                    // More complicated: need to keep all affiliated tasks together.
-                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
-                        // All went well.
-                        return;
-                    }
-
-                    // Uh oh...  something bad in the affiliation chain, try to rebuild
-                    // everything and then go through our general path of adding a new task.
-                    needAffiliationFix = true;
-                }
-            } else {
-                Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
-                needAffiliationFix = true;
+            if (!printedHeader.value) {
+                pw.println("  Recent tasks:");
+                printedHeader.value = true;
+                printedAnything.value = true;
+            }
+            pw.print("  * Recent #"); pw.print(i); pw.print(": ");
+            pw.println(tr);
+            if (dumpAll) {
+                tr.dump(pw, "    ");
             }
         }
 
-        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
-        trimForTaskLocked(task, true);
-
-        recentsCount = size();
-        final int maxRecents = ActivityManager.getMaxRecentTasksStatic();
-        while (recentsCount >= maxRecents) {
-            final TaskRecord tr = remove(recentsCount - 1);
-            tr.removedFromRecents();
-            recentsCount--;
-        }
-        task.inRecents = true;
-        if (!isAffiliated || needAffiliationFix) {
-            // If this is a simple non-affiliated task, or we had some failure trying to
-            // handle it as part of an affilated task, then just place it at the top.
-            add(0, task);
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
-        } else if (isAffiliated) {
-            // If this is a new affiliated task, then move all of the affiliated tasks
-            // to the front and insert this new one.
-            TaskRecord other = task.mNextAffiliate;
-            if (other == null) {
-                other = task.mPrevAffiliate;
-            }
-            if (other != null) {
-                int otherIndex = indexOf(other);
-                if (otherIndex >= 0) {
-                    // Insert new task at appropriate location.
-                    int taskIndex;
-                    if (other == task.mNextAffiliate) {
-                        // We found the index of our next affiliation, which is who is
-                        // before us in the list, so add after that point.
-                        taskIndex = otherIndex+1;
-                    } else {
-                        // We found the index of our previous affiliation, which is who is
-                        // after us in the list, so add at their position.
-                        taskIndex = otherIndex;
-                    }
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "addRecent: new affiliated task added at " + taskIndex + ": " + task);
-                    add(taskIndex, task);
-
-                    // Now move everything to the front.
-                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
-                        // All went well.
-                        return;
-                    }
-
-                    // Uh oh...  something bad in the affiliation chain, try to rebuild
-                    // everything and then go through our general path of adding a new task.
-                    needAffiliationFix = true;
-                } else {
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "addRecent: couldn't find other affiliation " + other);
-                    needAffiliationFix = true;
-                }
-            } else {
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                        "addRecent: adding affiliated task without next/prev:" + task);
-                needAffiliationFix = true;
-            }
-        }
-
-        if (needAffiliationFix) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
-            cleanupLocked(task.userId);
+        if (!printedAnything.value) {
+            pw.println("  (nothing)");
         }
     }
 
     /**
-     * If needed, remove oldest existing entries in recents that are for the same kind
-     * of task as the given one.
+     * Creates a new RecentTaskInfo from a TaskRecord.
      */
-    int trimForTaskLocked(TaskRecord task, boolean doTrim) {
-        int recentsCount = size();
-        final Intent intent = task.intent;
-        final boolean document = intent != null && intent.isDocument();
-        int maxRecents = task.maxRecents - 1;
-        final ActivityStack stack = task.getStack();
-        for (int i = 0; i < recentsCount; i++) {
-            final TaskRecord tr = get(i);
-            final ActivityStack trStack = tr.getStack();
-            if (task != tr) {
-                if (stack != null && trStack != null && stack != trStack) {
-                    continue;
-                }
-                if (task.userId != tr.userId) {
-                    continue;
-                }
-                final Intent trIntent = tr.intent;
-                final boolean sameAffinity =
-                        task.affinity != null && task.affinity.equals(tr.affinity);
-                final boolean sameIntentFilter = intent != null && intent.filterEquals(trIntent);
-                boolean multiTasksAllowed = false;
-                final int flags = intent.getFlags();
-                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
-                        && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                    multiTasksAllowed = true;
-                }
-                final boolean trIsDocument = trIntent != null && trIntent.isDocument();
-                final boolean bothDocuments = document && trIsDocument;
-                if (!sameAffinity && !sameIntentFilter && !bothDocuments) {
-                    continue;
-                }
+    static ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
+        // Update the task description to reflect any changes in the task stack
+        tr.updateTaskDescription();
 
-                if (bothDocuments) {
-                    // Do these documents belong to the same activity?
-                    final boolean sameActivity = task.realActivity != null
-                            && tr.realActivity != null
-                            && task.realActivity.equals(tr.realActivity);
-                    // If the document is open in another app or is not the same
-                    // document, we don't need to trim it.
-                    if (!sameActivity) {
-                        continue;
-                    // Otherwise only trim if we are over our max recents for this task
-                    } else if (maxRecents > 0) {
-                        --maxRecents;
-                        if (!doTrim || !sameIntentFilter || multiTasksAllowed) {
-                            // We don't want to trim if we are not over the max allowed entries and
-                            // the caller doesn't want us to trim, the tasks are not of the same
-                            // intent filter, or multiple entries fot the task is allowed.
-                            continue;
-                        }
-                    }
-                    // Hit the maximum number of documents for this task. Fall through
-                    // and remove this document from recents.
-                } else if (document || trIsDocument) {
-                    // Only one of these is a document. Not the droid we're looking for.
-                    continue;
-                }
-            }
+        // Compose the recent task info
+        ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
+        rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
+        rti.persistentId = tr.taskId;
+        rti.baseIntent = new Intent(tr.getBaseIntent());
+        rti.origActivity = tr.origActivity;
+        rti.realActivity = tr.realActivity;
+        rti.description = tr.lastDescription;
+        rti.stackId = tr.getStackId();
+        rti.userId = tr.userId;
+        rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
+        rti.lastActiveTime = tr.lastActiveTime;
+        rti.affiliatedTaskId = tr.mAffiliatedTaskId;
+        rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
+        rti.numActivities = 0;
+        if (tr.mBounds != null) {
+            rti.bounds = new Rect(tr.mBounds);
+        }
+        rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
+        rti.resizeMode = tr.mResizeMode;
+        rti.configuration.setTo(tr.getConfiguration());
 
-            if (!doTrim) {
-                // If the caller is not actually asking for a trim, just tell them we reached
-                // a point where the trim would happen.
-                return i;
-            }
+        ActivityRecord base = null;
+        ActivityRecord top = null;
+        ActivityRecord tmp;
 
-            // Either task and tr are the same or, their affinities match or their intents match
-            // and neither of them is a document, or they are documents using the same activity
-            // and their maxRecents has been reached.
-            remove(i);
-            if (task != tr) {
-                tr.removedFromRecents();
+        for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
+            tmp = tr.mActivities.get(i);
+            if (tmp.finishing) {
+                continue;
             }
-            i--;
-            recentsCount--;
-            if (task.intent == null) {
-                // If the new recent task we are adding is not fully
-                // specified, then replace it with the existing recent task.
-                task = tr;
+            base = tmp;
+            if (top == null || (top.state == ActivityState.INITIALIZING)) {
+                top = base;
             }
-            notifyTaskPersisterLocked(tr, false);
+            rti.numActivities++;
         }
 
-        return -1;
-    }
+        rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
+        rti.topActivity = (top != null) ? top.intent.getComponent() : null;
 
-    // Sort by taskId
-    private static Comparator<TaskRecord> sTaskRecordComparator = new Comparator<TaskRecord>() {
-        @Override
-        public int compare(TaskRecord lhs, TaskRecord rhs) {
-            return rhs.taskId - lhs.taskId;
-        }
-    };
-
-    // Extract the affiliates of the chain containing recent at index start.
-    private int processNextAffiliateChainLocked(int start) {
-        final TaskRecord startTask = get(start);
-        final int affiliateId = startTask.mAffiliatedTaskId;
-
-        // Quick identification of isolated tasks. I.e. those not launched behind.
-        if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
-                startTask.mNextAffiliate == null) {
-            // There is still a slim chance that there are other tasks that point to this task
-            // and that the chain is so messed up that this task no longer points to them but
-            // the gain of this optimization outweighs the risk.
-            startTask.inRecents = true;
-            return start + 1;
-        }
-
-        // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
-        mTmpRecents.clear();
-        for (int i = size() - 1; i >= start; --i) {
-            final TaskRecord task = get(i);
-            if (task.mAffiliatedTaskId == affiliateId) {
-                remove(i);
-                mTmpRecents.add(task);
-            }
-        }
-
-        // Sort them all by taskId. That is the order they were create in and that order will
-        // always be correct.
-        Collections.sort(mTmpRecents, sTaskRecordComparator);
-
-        // Go through and fix up the linked list.
-        // The first one is the end of the chain and has no next.
-        final TaskRecord first = mTmpRecents.get(0);
-        first.inRecents = true;
-        if (first.mNextAffiliate != null) {
-            Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
-            first.setNextAffiliate(null);
-            notifyTaskPersisterLocked(first, false);
-        }
-        // Everything in the middle is doubly linked from next to prev.
-        final int tmpSize = mTmpRecents.size();
-        for (int i = 0; i < tmpSize - 1; ++i) {
-            final TaskRecord next = mTmpRecents.get(i);
-            final TaskRecord prev = mTmpRecents.get(i + 1);
-            if (next.mPrevAffiliate != prev) {
-                Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
-                        " setting prev=" + prev);
-                next.setPrevAffiliate(prev);
-                notifyTaskPersisterLocked(next, false);
-            }
-            if (prev.mNextAffiliate != next) {
-                Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
-                        " setting next=" + next);
-                prev.setNextAffiliate(next);
-                notifyTaskPersisterLocked(prev, false);
-            }
-            prev.inRecents = true;
-        }
-        // The last one is the beginning of the list and has no prev.
-        final TaskRecord last = mTmpRecents.get(tmpSize - 1);
-        if (last.mPrevAffiliate != null) {
-            Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
-            last.setPrevAffiliate(null);
-            notifyTaskPersisterLocked(last, false);
-        }
-
-        // Insert the group back into mRecentTasks at start.
-        addAll(start, mTmpRecents);
-        mTmpRecents.clear();
-
-        // Let the caller know where we left off.
-        return start + tmpSize;
+        return rti;
     }
 }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ac85e6b..16995e5 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -33,6 +33,7 @@
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -517,14 +518,27 @@
                             } catch (PackageManager.NameNotFoundException e) {
                             }
                         }
-                        if (localForegroundNoti.getSmallIcon() == null
-                                || nm.getNotificationChannel(localPackageName, appUid,
+                        if (nm.getNotificationChannel(localPackageName, appUid,
                                 localForegroundNoti.getChannelId()) == null) {
+                            int targetSdkVersion = Build.VERSION_CODES.O_MR1;
+                            try {
+                                final ApplicationInfo applicationInfo =
+                                        ams.mContext.getPackageManager().getApplicationInfoAsUser(
+                                                appInfo.packageName, 0, userId);
+                                targetSdkVersion = applicationInfo.targetSdkVersion;
+                            } catch (PackageManager.NameNotFoundException e) {
+                            }
+                            if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
+                                throw new RuntimeException(
+                                        "invalid channel for service notification: "
+                                                + foregroundNoti);
+                            }
+                        }
+                        if (localForegroundNoti.getSmallIcon() == null) {
                             // Notifications whose icon is 0 are defined to not show
                             // a notification, silently ignoring it.  We don't want to
                             // just ignore it, we want to prevent the service from
                             // being foreground.
-                            // Also every notification needs a channel.
                             throw new RuntimeException("invalid service notification: "
                                     + foregroundNoti);
                         }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 61994b5..2689d6a 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -567,7 +567,7 @@
         SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
         synchronized (mService) {
             for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
-                SparseBooleanArray taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId);
+                SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
                 SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
                 if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
                     continue;
@@ -640,7 +640,7 @@
         @Override
         public void run() {
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-            ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
+            ArraySet<Integer> persistentTaskIds = new ArraySet<>();
             while (true) {
                 // We can't lock mService while holding TaskPersister.this, but we don't want to
                 // call removeObsoleteFiles every time through the loop, only the last time before
@@ -654,20 +654,7 @@
                     persistentTaskIds.clear();
                     synchronized (mService) {
                         if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
-                        for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                            final TaskRecord task = mRecentTasks.get(taskNdx);
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
-                                    " persistable=" + task.isPersistable);
-                            final ActivityStack stack = task.getStack();
-                            if ((task.isPersistable || task.inRecents)
-                                    && (stack == null || !stack.isHomeOrRecentsStack())) {
-                                if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
-                                persistentTaskIds.add(task.taskId);
-                            } else {
-                                if (DEBUG) Slog.d(TAG,
-                                        "omitting from persistentTaskIds task=" + task);
-                            }
-                        }
+                        mRecentTasks.getPersistableTaskIds(persistentTaskIds);
                         mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
                                 mRecentTasks.usersWithRecentsLoadedLocked());
                     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0d8df79..7817f1a 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,10 +18,7 @@
 
 import static android.app.ActivityManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-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.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -31,6 +28,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -102,6 +100,7 @@
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -155,8 +154,6 @@
     private static final String ATTR_EFFECTIVE_UID = "effective_uid";
     @Deprecated
     private static final String ATTR_TASKTYPE = "task_type";
-    private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
-    private static final String ATTR_LASTACTIVETIME = "last_active_time";
     private static final String ATTR_LASTDESCRIPTION = "last_description";
     private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
     private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
@@ -212,9 +209,10 @@
     ComponentName realActivity; // The actual activity component that started the task.
     boolean realActivitySuspended; // True if the actual activity component that started the
                                    // task is suspended.
-    long firstActiveTime;   // First time this task was active.
-    long lastActiveTime;    // Last time this task was active, including sleep.
     boolean inRecents;      // Actually in the recents list?
+    long lastActiveTime;    // Last time this task was active in the current device session,
+                            // including sleep. This time is initialized to the elapsed time when
+                            // restored from disk.
     boolean isAvailable;    // Is the activity available to be launched?
     boolean rootWasReset;   // True if the intent at the root of the task had
                             // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
@@ -339,6 +337,7 @@
                 TaskPersister.IMAGE_EXTENSION;
         userId = UserHandle.getUserId(info.applicationInfo.uid);
         taskId = _taskId;
+        lastActiveTime = SystemClock.elapsedRealtime();
         mAffiliatedTaskId = _taskId;
         voiceSession = _voiceSession;
         voiceInteractor = _voiceInteractor;
@@ -359,6 +358,7 @@
                 TaskPersister.IMAGE_EXTENSION;
         userId = UserHandle.getUserId(info.applicationInfo.uid);
         taskId = _taskId;
+        lastActiveTime = SystemClock.elapsedRealtime();
         mAffiliatedTaskId = _taskId;
         voiceSession = null;
         voiceInteractor = null;
@@ -385,12 +385,12 @@
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
             boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
             int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
-            long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
-            boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
-            int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-            int callingUid, String callingPackage, int resizeMode, boolean supportsPictureInPicture,
-            boolean privileged, boolean _realActivitySuspended, boolean userSetupComplete,
-            int minWidth, int minHeight) {
+            long lastTimeMoved, boolean neverRelinquishIdentity,
+            TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
+            int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+            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;
@@ -412,8 +412,7 @@
         userId = _userId;
         mUserSetupComplete = userSetupComplete;
         effectiveUid = _effectiveUid;
-        firstActiveTime = _firstActiveTime;
-        lastActiveTime = _lastActiveTime;
+        lastActiveTime = SystemClock.elapsedRealtime();
         lastDescription = _lastDescription;
         mActivities = activities;
         mLastTimeMoved = lastTimeMoved;
@@ -520,7 +519,7 @@
             // All we can do for now is update the bounds so it can be used when the task is
             // added to window manager.
             updateOverrideConfiguration(bounds);
-            if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
+            if (!inFreeformWindowingMode()) {
                 // re-restore the task so it can have the proper stack association.
                 mService.mStackSupervisor.restoreRecentTaskLocked(this, null);
             }
@@ -616,8 +615,7 @@
      * @return whether the task was reparented
      */
     // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
-    // re-parenting the task. Can only be done when we are no longer using static stack Ids like
-    /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */
+    // re-parenting the task. Can only be done when we are no longer using static stack Ids.
     boolean reparent(ActivityStack preferredStack, int position,
             @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
             boolean schedulePictureInPictureModeChange, String reason) {
@@ -630,12 +628,12 @@
             return false;
         }
 
-        final int sourceStackId = getStackId();
-        final int stackId = toStack.getStackId();
+        final int toStackWindowingMode = toStack.getWindowingMode();
         final ActivityRecord topActivity = getTopActivity();
 
-        final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
-                && topActivity != null;
+        final boolean mightReplaceWindow =
+                replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode)
+                        && topActivity != null;
         if (mightReplaceWindow) {
             // We are about to relaunch the activity because its configuration changed due to
             // being maximized, i.e. size change. The activity will first remove the old window
@@ -707,10 +705,10 @@
             toStack.prepareFreezingTaskBounds();
 
             // Make sure the task has the appropriate bounds/size for the stack it is in.
-            final int toStackWindowingMode = toStack.getWindowingMode();
             final boolean toStackSplitScreenPrimary =
                     toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-            if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
+            if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
+                    || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
                     && !Objects.equals(mBounds, toStack.mBounds)) {
                 kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
                         deferResume);
@@ -751,7 +749,7 @@
         // TODO: Handle incorrect request to move before the actual move, not after.
         final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
         supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
-                DEFAULT_DISPLAY, stackId);
+                DEFAULT_DISPLAY, toStack.mStackId);
 
         boolean successful = (preferredStack == toStack);
         if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -761,6 +759,18 @@
         return successful;
     }
 
+    /**
+     * 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.
+     * @hide
+     */
+    private static boolean replaceWindowsOnTaskMove(
+            int sourceWindowingMode, int targetWindowingMode) {
+        return sourceWindowingMode == WINDOWING_MODE_FREEFORM
+                || targetWindowingMode == WINDOWING_MODE_FREEFORM;
+    }
+
     void cancelWindowTransition() {
         mWindowContainerController.cancelWindowTransition();
     }
@@ -780,14 +790,11 @@
     }
 
     void touchActiveTime() {
-        lastActiveTime = System.currentTimeMillis();
-        if (firstActiveTime == 0) {
-            firstActiveTime = lastActiveTime;
-        }
+        lastActiveTime = SystemClock.elapsedRealtime();
     }
 
     long getInactiveDuration() {
-        return System.currentTimeMillis() - lastActiveTime;
+        return SystemClock.elapsedRealtime() - lastActiveTime;
     }
 
     /** Sets the original intent, and the calling uid and package. */
@@ -1263,7 +1270,7 @@
             mService.notifyTaskPersisterLocked(this, false);
         }
 
-        if (getStackId() == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             // We normally notify listeners of task stack changes on pause, however pinned stack
             // activities are normally in the paused state so no notification will be sent there
             // before the activity is removed. We send it here so instead.
@@ -1493,7 +1500,7 @@
      * @return True if the requested bounds are okay for a resizing request.
      */
     private boolean canResizeToBounds(Rect bounds) {
-        if (bounds == null || getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
+        if (bounds == null || !inFreeformWindowingMode()) {
             // Note: If not on the freeform workspace, we ignore the bounds.
             return true;
         }
@@ -1647,8 +1654,6 @@
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
         out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
         out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
-        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
         out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
         out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
         if (lastDescription != null) {
@@ -1721,8 +1726,6 @@
         boolean userSetupComplete = true;
         int effectiveUid = -1;
         String lastDescription = null;
-        long firstActiveTime = -1;
-        long lastActiveTime = -1;
         long lastTimeOnTop = 0;
         boolean neverRelinquishIdentity = true;
         int taskId = INVALID_TASK_ID;
@@ -1774,10 +1777,6 @@
                 effectiveUid = Integer.parseInt(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
                 taskType = Integer.parseInt(attrValue);
-            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
-                firstActiveTime = Long.parseLong(attrValue);
-            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
-                lastActiveTime = Long.parseLong(attrValue);
             } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
                 lastDescription = attrValue;
             } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
@@ -1888,9 +1887,9 @@
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
                 autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
-                taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage, resizeMode, supportsPictureInPicture, privileged,
+                activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
+                taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
+                callingPackage, resizeMode, supportsPictureInPicture, privileged,
                 realActivitySuspended, userSetupComplete, minWidth, minHeight);
         task.updateOverrideConfiguration(bounds);
 
@@ -1911,7 +1910,7 @@
         // If the task has no requested minimal size, we'd like to enforce a minimal size
         // so that the user can not render the task too small to manipulate. We don't need
         // to do this for the pinned stack as the bounds are controlled by the system.
-        if (getStackId() != PINNED_STACK_ID) {
+        if (!inPinnedWindowingMode()) {
             if (minWidth == INVALID_MIN_SIZE) {
                 minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
             }
@@ -2085,7 +2084,7 @@
             return;
         }
 
-        if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+        if (inStack.inFreeformWindowingMode()) {
             if (!isResizeable()) {
                 throw new IllegalArgumentException("Can not position non-resizeable task="
                         + this + " in stack=" + inStack);
@@ -2220,7 +2219,6 @@
                 pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
                 pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
                 pw.print(" isResizeable=" + isResizeable());
-                pw.print(" firstActiveTime=" + firstActiveTime);
                 pw.print(" lastActiveTime=" + lastActiveTime);
                 pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
     }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 6506cf7..4943173 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -184,11 +184,15 @@
         }
     }
 
+    private static final int FLAGS_FOR_SILENCE_OVERRIDE =
+            AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
+            AudioAttributes.FLAG_BYPASS_MUTE;
+
     private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
         if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
                 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
-            if ((apc.getAudioAttributes().getAllFlags() &
-                    AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 &&
+            if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
+                        == FLAGS_FOR_SILENCE_OVERRIDE  &&
                     apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
                     mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
                             apc.getClientPid(), apc.getClientUid()) ==
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 5583e86..d7cd81f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1371,6 +1371,7 @@
                     sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
                 }
             }
+            mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null);
             setUpstreamNetwork(ns);
         }
 
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 c5f7528..b35ed75 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -95,7 +95,10 @@
     private NetworkCallback mDefaultNetworkCallback;
     private NetworkCallback mMobileNetworkCallback;
     private boolean mDunRequired;
-    private Network mCurrentDefault;
+    // The current system default network (not really used yet).
+    private Network mDefaultInternetNetwork;
+    // The current upstream network used for tethering.
+    private Network mTetheringUpstreamNetwork;
 
     public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
         mContext = ctx;
@@ -130,10 +133,12 @@
 
         releaseCallback(mDefaultNetworkCallback);
         mDefaultNetworkCallback = null;
+        mDefaultInternetNetwork = null;
 
         releaseCallback(mListenAllCallback);
         mListenAllCallback = null;
 
+        mTetheringUpstreamNetwork = null;
         mNetworkMap.clear();
     }
 
@@ -207,7 +212,7 @@
                 break;
             default:
                 /* If we've found an active upstream connection that's not DUN/HIPRI
-                 * we should stop any outstanding DUN/HIPRI start requests.
+                 * we should stop any outstanding DUN/HIPRI requests.
                  *
                  * 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.
@@ -219,6 +224,10 @@
         return typeStatePair.ns;
     }
 
+    public void setCurrentUpstream(Network upstream) {
+        mTetheringUpstreamNetwork = upstream;
+    }
+
     public Set<IpPrefix> getLocalPrefixes() {
         return (Set<IpPrefix>) mLocalPrefixes.clone();
     }
@@ -250,7 +259,7 @@
                     // These request*() calls can be deleted post oag/339444.
                     return;
                 }
-                mCurrentDefault = network;
+                mDefaultInternetNetwork = network;
                 break;
 
             case CALLBACK_MOBILE_REQUEST:
@@ -302,6 +311,13 @@
                     network, newNc));
         }
 
+        // Log changes in upstream network signal strength, if available.
+        if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
+            final int newSignal = newNc.getSignalStrength();
+            final String prevSignal = getSignalStrength(prev.networkCapabilities);
+            mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
+        }
+
         mNetworkMap.put(network, new NetworkState(
                 null, prev.linkProperties, newNc, network, null, null));
         // TODO: If sufficient information is available to select a more
@@ -330,9 +346,21 @@
         notifyTarget(EVENT_ON_LINKPROPERTIES, network);
     }
 
+    private void handleSuspended(int callbackType, Network network) {
+        if (callbackType != CALLBACK_LISTEN_ALL) return;
+        if (!network.equals(mTetheringUpstreamNetwork)) return;
+        mLog.log("SUSPENDED current upstream: " + network);
+    }
+
+    private void handleResumed(int callbackType, Network network) {
+        if (callbackType != CALLBACK_LISTEN_ALL) return;
+        if (!network.equals(mTetheringUpstreamNetwork)) return;
+        mLog.log("RESUMED current upstream: " + network);
+    }
+
     private void handleLost(int callbackType, Network network) {
         if (callbackType == CALLBACK_TRACK_DEFAULT) {
-            mCurrentDefault = null;
+            mDefaultInternetNetwork = null;
             // Receiving onLost() for a default network does not necessarily
             // mean the network is gone.  We wait for a separate notification
             // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
@@ -401,8 +429,15 @@
             recomputeLocalPrefixes();
         }
 
-        // TODO: Handle onNetworkSuspended();
-        // TODO: Handle onNetworkResumed();
+        @Override
+        public void onNetworkSuspended(Network network) {
+            handleSuspended(mCallbackType, network);
+        }
+
+        @Override
+        public void onNetworkResumed(Network network) {
+            handleResumed(mCallbackType, network);
+        }
 
         @Override
         public void onLost(Network network) {
@@ -467,4 +502,9 @@
 
         return prefixSet;
     }
+
+    private static String getSignalStrength(NetworkCapabilities nc) {
+        if (nc == null || !nc.hasSignalStrength()) return "unknown";
+        return Integer.toString(nc.getSignalStrength());
+    }
 }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 9aabdab..9a6e609 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -29,6 +29,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.text.format.DateUtils;
 import android.util.EventLog;
 import android.util.MathUtils;
@@ -304,6 +305,7 @@
     }
 
     private void handleLightSensorEvent(long time, float lux) {
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
         mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
 
         if (mAmbientLightRingBuffer.size() == 0) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f8e5836..f930b52 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -683,8 +683,8 @@
         // Configure auto-brightness.
         boolean autoBrightnessEnabled = false;
         if (mAutomaticBrightnessController != null) {
-            final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
-                    && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
+            final boolean autoBrightnessEnabledInDoze =
+                    mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
             autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                     && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                     && brightness < 0;
@@ -726,8 +726,7 @@
         }
 
         // Use default brightness when dozing unless overridden.
-        if (brightness < 0 && (state == Display.STATE_DOZE
-                || state == Display.STATE_DOZE_SUSPEND)) {
+        if (brightness < 0 && Display.isDozeState(state)) {
             brightness = mScreenBrightnessDozeConfig;
         }
 
@@ -777,7 +776,6 @@
         // Skip the animation when the screen is off or suspended or transition to/from VR.
         if (!mPendingScreenOff) {
             if (mSkipScreenOnBrightnessRamp) {
-
                 if (state == Display.STATE_ON) {
                     if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
                         mInitialAutoBrightness = brightness;
@@ -794,15 +792,25 @@
                 }
             }
 
-            boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR);
-            if ((state == Display.STATE_ON
-                    && mSkipRampState == RAMP_STATE_SKIP_NONE
-                    || state == Display.STATE_DOZE && !mBrightnessBucketsInDozeConfig)
-                    && !wasOrWillBeInVr) {
+            final boolean wasOrWillBeInVr =
+                    (state == Display.STATE_VR || oldState == Display.STATE_VR);
+            final boolean initialRampSkip =
+                    state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE;
+            // While dozing, sometimes the brightness is split into buckets. Rather than animating
+            // through the buckets, which is unlikely to be smooth in the first place, just jump
+            // right to the suggested brightness.
+            final boolean hasBrightnessBuckets =
+                    Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
+            // If the color fade is totally covering the screen then we can change the backlight
+            // level without it being a noticeable jump since any actual content isn't yet visible.
+            final boolean isDisplayContentVisible =
+                    mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
+            if (initialRampSkip || hasBrightnessBuckets
+                    || wasOrWillBeInVr || !isDisplayContentVisible) {
+                animateScreenBrightness(brightness, 0);
+            } else {
                 animateScreenBrightness(brightness,
                         slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
-            } else {
-                animateScreenBrightness(brightness, 0);
             }
         }
 
@@ -925,6 +933,7 @@
             }
 
             if (!reportOnly) {
+                Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 mPowerState.setScreenState(state);
                 // Tell battery stats about the transition.
                 try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0b11479..664d2f9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -784,6 +784,14 @@
                 mService.enforcePhoneStatePermission(pid, uid);
             }
             mFlags = flags;
+            if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mService.setGlobalPrioritySession(MediaSessionRecord.this);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
             mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b102dde..aa65244 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -178,17 +178,6 @@
                 return;
             }
             if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
-                if (mGlobalPrioritySession != record) {
-                    Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
-                            + " to " + record);
-                    mGlobalPrioritySession = record;
-                    if (user != null && user.mPriorityStack.contains(record)) {
-                        // Handle the global priority session separately.
-                        // Otherwise, it will be the media button session even after it becomes
-                        // inactive because it has been the lastly played media app.
-                        user.mPriorityStack.removeSession(record);
-                    }
-                }
                 if (DEBUG_KEY_EVENT) {
                     Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
                 }
@@ -204,6 +193,24 @@
         }
     }
 
+    public void setGlobalPrioritySession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (mGlobalPrioritySession != record) {
+                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+                        + " to " + record);
+                mGlobalPrioritySession = record;
+                if (user != null && user.mPriorityStack.contains(record)) {
+                    // Handle the global priority session separately.
+                    // Otherwise, it can be the media button session regardless of the active state
+                    // because it or other system components might have been the lastly played media
+                    // app.
+                    user.mPriorityStack.removeSession(record);
+                }
+            }
+        }
+    }
+
     private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
         List<MediaSessionRecord> records = new ArrayList<>();
         if (userId == UserHandle.USER_ALL) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 14cd055..168f070 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -282,6 +282,7 @@
     private WindowManagerInternal mWindowManagerInternal;
     private AlarmManager mAlarmManager;
     private ICompanionDeviceManager mCompanionManager;
+    private AccessibilityManager mAccessibilityManager;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -1221,6 +1222,12 @@
         mUsageStats = us;
     }
 
+    @VisibleForTesting
+    void setAccessibilityManager(AccessibilityManager am) {
+        mAccessibilityManager = am;
+    }
+
+
     // TODO: All tests should use this init instead of the one-off setters above.
     @VisibleForTesting
     void init(Looper looper, IPackageManager packageManager,
@@ -1235,6 +1242,8 @@
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
 
+        mAccessibilityManager =
+                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
         mAm = ActivityManager.getService();
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
@@ -4117,13 +4126,16 @@
         // These are set inside the conditional if the notification is allowed to make noise.
         boolean hasValidVibrate = false;
         boolean hasValidSound = false;
+        boolean sentAccessibilityEvent = false;
+        // If the notification will appear in the status bar, it should send an accessibility
+        // event
+        if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
+            sendAccessibilityEvent(notification, record.sbn.getPackageName());
+            sentAccessibilityEvent = true;
+        }
 
         if (aboveThreshold && isNotificationForCurrentUser(record)) {
-            // If the notification will appear in the status bar, it should send an accessibility
-            // event
-            if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
-                sendAccessibilityEvent(notification, record.sbn.getPackageName());
-            }
+
             if (mSystemReady && mAudioManager != null) {
                 Uri soundUri = record.getSound();
                 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -4141,6 +4153,10 @@
 
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
+                    if (!sentAccessibilityEvent) {
+                        sendAccessibilityEvent(notification, record.sbn.getPackageName());
+                        sentAccessibilityEvent = true;
+                    }
                     if (DBG) Slog.v(TAG, "Interrupting!");
                     if (hasValidSound) {
                         mSoundNotificationKey = key;
@@ -4661,8 +4677,7 @@
     }
 
     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
-        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
-        if (!manager.isEnabled()) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
 
@@ -4676,7 +4691,7 @@
             event.getText().add(tickerText);
         }
 
-        manager.sendAccessibilityEvent(event);
+        mAccessibilityManager.sendAccessibilityEvent(event);
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index a7a2743..abf2900 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -117,15 +117,19 @@
                 ZenLog.traceIntercepted(record, "alarmsOnly");
                 return true;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                if (isAlarm(record)) {
-                    // Alarms are always priority
-                    return false;
-                }
                 // allow user-prioritized packages through in priority mode
                 if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
                     ZenLog.traceNotIntercepted(record, "priorityApp");
                     return false;
                 }
+
+                if (isAlarm(record)) {
+                    if (!config.allowAlarms) {
+                        ZenLog.traceIntercepted(record, "!allowAlarms");
+                        return true;
+                    }
+                    return false;
+                }
                 if (isCall(record)) {
                     if (config.allowRepeatCallers
                             && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
@@ -159,6 +163,15 @@
                     }
                     return false;
                 }
+                AudioAttributes aa = record.getAudioAttributes();
+                if (aa != null && AudioAttributes.SUPPRESSIBLE_USAGES.get(aa.getUsage()) ==
+                        AudioAttributes.SUPPRESSIBLE_MEDIA_SYSTEM_OTHER) {
+                    if (!config.allowMediaSystemOther) {
+                        ZenLog.traceIntercepted(record, "!allowMediaSystemOther");
+                        return true;
+                    }
+                    return false;
+                }
                 ZenLog.traceIntercepted(record, "!priority");
                 return true;
             default:
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9fcc67d..710684f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -59,6 +59,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.server.LocalServices;
 
@@ -87,7 +88,7 @@
     private final Context mContext;
     private final H mHandler;
     private final SettingsObserver mSettingsObserver;
-    private final AppOpsManager mAppOps;
+    @VisibleForTesting protected final AppOpsManager mAppOps;
     protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
@@ -102,9 +103,9 @@
     private final String SCHEDULED_DEFAULT_RULE_1 = "SCHEDULED_DEFAULT_RULE_1";
     private final String SCHEDULED_DEFAULT_RULE_2 = "SCHEDULED_DEFAULT_RULE_2";
 
-    private int mZenMode;
+    @VisibleForTesting protected int mZenMode;
     private int mUser = UserHandle.USER_SYSTEM;
-    protected ZenModeConfig mConfig;
+    @VisibleForTesting protected ZenModeConfig mConfig;
     private AudioManagerInternal mAudioManager;
     protected PackageManager mPm;
     private long mSuppressedEffects;
@@ -595,8 +596,9 @@
             pw.println(config);
             return;
         }
-        pw.printf("allow(calls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s,"
+        pw.printf("allow(alarms=%b,media=%bcalls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s,"
                 + "events=%b,reminders=%b,whenScreenOff=%b,whenScreenOn=%b)\n",
+                config.allowAlarms, config.allowMediaSystemOther,
                 config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
                 config.allowRepeatCallers, config.allowMessages,
                 ZenModeConfig.sourceToString(config.allowMessagesFrom),
@@ -813,7 +815,8 @@
         }
     }
 
-    private void applyRestrictions() {
+    @VisibleForTesting
+    protected void applyRestrictions() {
         final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
 
         // notification restrictions
@@ -822,6 +825,10 @@
         // call restrictions
         final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
                 || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
+        // alarm restrictions
+        final boolean muteAlarms = zen && !mConfig.allowAlarms;
+        // alarm restrictions
+        final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
         // total silence restrictions
         final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
 
@@ -833,13 +840,18 @@
                 applyRestrictions(muteNotifications || muteEverything, usage);
             } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
                 applyRestrictions(muteCalls || muteEverything, usage);
+            } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) {
+                applyRestrictions(muteAlarms || muteEverything, usage);
+            } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA_SYSTEM_OTHER) {
+                applyRestrictions(muteMediaAndSystemSounds || muteEverything, usage);
             } else {
                 applyRestrictions(muteEverything, usage);
             }
         }
     }
 
-    private void applyRestrictions(boolean mute, int usage) {
+    @VisibleForTesting
+    protected void applyRestrictions(boolean mute, int usage) {
         final String[] exceptionPackages = null; // none (for now)
         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
                 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2d8eba2..d7329db 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -398,7 +398,7 @@
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
     private static final boolean DEBUG_BACKUP = false;
     private static final boolean DEBUG_INSTALL = false;
-    private static final boolean DEBUG_REMOVE = false;
+    public static final boolean DEBUG_REMOVE = false;
     private static final boolean DEBUG_BROADCASTS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
     private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -406,7 +406,7 @@
     public static final boolean DEBUG_PACKAGE_SCANNING = false;
     private static final boolean DEBUG_VERIFY = false;
     private static final boolean DEBUG_FILTERS = false;
-    private static final boolean DEBUG_PERMISSIONS = false;
+    public static final boolean DEBUG_PERMISSIONS = false;
     private static final boolean DEBUG_SHARED_LIBRARIES = false;
     private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
 
@@ -954,9 +954,6 @@
     final SparseArray<PackageVerificationState> mPendingVerification
             = new SparseArray<PackageVerificationState>();
 
-    /** Set of packages associated with each app op permission. */
-    final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
-
     final PackageInstallerService mInstallerService;
 
     private final PackageDexOptimizer mPackageDexOptimizer;
@@ -2881,7 +2878,7 @@
                         + mSdkVersion + "; regranting permissions for internal storage");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
+            updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
             ver.sdkVersion = mSdkVersion;
 
             // If this is the first boot or an update from pre-M, and it is a normal
@@ -3368,7 +3365,9 @@
     @Override
     public boolean isUpgrade() {
         // allow instant applications
-        return mIsUpgrade;
+        // The system property allows testing ota flow when upgraded to the same image.
+        return mIsUpgrade || SystemProperties.getBoolean(
+                "persist.pm.mock-upgrade", false /* default */);
     }
 
     private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
@@ -5182,7 +5181,7 @@
                 final PermissionsState permissionsState = settingBase.getPermissionsState();
                 if (permissionsState.hasPermission(permName, userId)) {
                     if (isUidInstantApp) {
-                        if (mPermissionManager.isPermissionInstant(permName)) {
+                        if (mSettings.mPermissions.isPermissionInstant(permName)) {
                             return PackageManager.PERMISSION_GRANTED;
                         }
                     } else {
@@ -5251,8 +5250,8 @@
         }
     }
 
-    boolean addPermission(PermissionInfo info, final boolean async) {
-        return mPermissionManager.addPermission(
+    private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
+        return mPermissionManager.addDynamicPermission(
                 info, async, getCallingUid(), new PermissionCallback() {
                     @Override
                     public void onPermissionChanged() {
@@ -5268,20 +5267,20 @@
     @Override
     public boolean addPermission(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermission(info, false);
+            return addDynamicPermission(info, false);
         }
     }
 
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermission(info, true);
+            return addDynamicPermission(info, true);
         }
     }
 
     @Override
     public void removePermission(String permName) {
-        mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
+        mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
     }
 
     @Override
@@ -5942,17 +5941,8 @@
     }
 
     @Override
-    public String[] getAppOpPermissionPackages(String permissionName) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return null;
-        }
-        synchronized (mPackages) {
-            ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
-            if (pkgs == null) {
-                return null;
-            }
-            return pkgs.toArray(new String[pkgs.size()]);
-        }
+    public String[] getAppOpPermissionPackages(String permName) {
+        return mPermissionManager.getAppOpPermissionPackages(permName);
     }
 
     @Override
@@ -10346,7 +10336,7 @@
                             Slog.i(TAG, "Adopting permissions from " + origName + " to "
                                     + pkg.packageName);
                             // SIDE EFFECTS; updates permissions system state; move elsewhere
-                            mSettings.transferPermissionsLPw(origName, pkg.packageName);
+                            mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
                         }
                     }
                 }
@@ -11194,54 +11184,13 @@
                 if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);
             }
 
-            N = pkg.permissions.size();
-            r = null;
-            for (i=0; i<N; i++) {
-                PackageParser.Permission p = pkg.permissions.get(i);
 
-                // Dont allow ephemeral apps to define new permissions.
-                if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                    Slog.w(TAG, "Permission " + p.info.name + " from package "
-                            + p.info.packageName
-                            + " ignored: instant apps cannot define new permissions.");
-                    continue;
-                }
-
-                // Assume by default that we did not install this permission into the system.
-                p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
-
-                // Now that permission groups have a special meaning, we ignore permission
-                // groups for legacy apps to prevent unexpected behavior. In particular,
-                // permissions for one app being granted to someone just because they happen
-                // to be in a group defined by another app (before this had no implications).
-                if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
-                    p.group = mPermissionGroups.get(p.info.group);
-                    // Warn for a permission in an unknown group.
-                    if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
-                        Slog.i(TAG, "Permission " + p.info.name + " from package "
-                                + p.info.packageName + " in an unknown group " + p.info.group);
-                    }
-                }
-
-                // TODO Move to PermissionManager once mPermissionTrees moves there.
-//                        p.tree ? mSettings.mPermissionTrees
-//                                : mSettings.mPermissions;
-//                final BasePermission bp = BasePermission.createOrUpdate(
-//                        permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
-//                permissionMap.put(p.info.name, bp);
-                if (p.tree) {
-                    final ArrayMap<String, BasePermission> permissionMap =
-                            mSettings.mPermissionTrees;
-                    final BasePermission bp = BasePermission.createOrUpdate(
-                            permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
-                            chatty);
-                    permissionMap.put(p.info.name, bp);
-                } else {
-                    final BasePermission bp = BasePermission.createOrUpdate(
-                            (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
-                            p, pkg, mSettings.mPermissionTrees, chatty);
-                    mPermissionManager.putPermissionTEMP(p.info.name, bp);
-                }
+            // Dont allow ephemeral apps to define new permissions.
+            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+                Slog.w(TAG, "Permissions from package " + pkg.packageName
+                        + " ignored: instant apps cannot define new permissions.");
+            } else {
+                mPermissionManager.addAllPermissions(pkg, chatty);
             }
 
             N = pkg.instrumentation.size();
@@ -11967,53 +11916,7 @@
             if (DEBUG_REMOVE) Log.d(TAG, "  Activities: " + r);
         }
 
-        N = pkg.permissions.size();
-        r = null;
-        for (i=0; i<N; i++) {
-            PackageParser.Permission p = pkg.permissions.get(i);
-            BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
-            if (bp == null) {
-                bp = mSettings.mPermissionTrees.get(p.info.name);
-            }
-            if (bp != null && bp.isPermission(p)) {
-                bp.setPermission(null);
-                if (DEBUG_REMOVE && chatty) {
-                    if (r == null) {
-                        r = new StringBuilder(256);
-                    } else {
-                        r.append(' ');
-                    }
-                    r.append(p.info.name);
-                }
-            }
-            if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
-                ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name);
-                if (appOpPkgs != null) {
-                    appOpPkgs.remove(pkg.packageName);
-                }
-            }
-        }
-        if (r != null) {
-            if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
-        }
-
-        N = pkg.requestedPermissions.size();
-        r = null;
-        for (i=0; i<N; i++) {
-            String perm = pkg.requestedPermissions.get(i);
-            if (mPermissionManager.isPermissionAppOp(perm)) {
-                ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
-                if (appOpPkgs != null) {
-                    appOpPkgs.remove(pkg.packageName);
-                    if (appOpPkgs.isEmpty()) {
-                        mAppOpPermissionPackages.remove(perm);
-                    }
-                }
-            }
-        }
-        if (r != null) {
-            if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
-        }
+        mPermissionManager.removeAllPermissions(pkg, chatty);
 
         N = pkg.instrumentation.size();
         r = null;
@@ -12074,18 +11977,9 @@
         }
     }
 
-    private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
-        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
-            if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    static final int UPDATE_PERMISSIONS_ALL = 1<<0;
-    static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
-    static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+    public static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+    public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+    public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
 
     private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
         // Update the parent permissions
@@ -12101,10 +11995,10 @@
     private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
             int flags) {
         final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
-        updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+        updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags);
     }
 
-    private void updatePermissionsLPw(String changingPkg,
+    private void updatePermissionsLocked(String changingPkg,
             PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
         // TODO: Most of the methods exposing BasePermission internals [source package name,
         // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
@@ -12116,55 +12010,11 @@
         // normal permissions. Today, we need two separate loops because these BasePermission
         // objects are stored separately.
         // Make sure there are no dangling permission trees.
-        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
-        while (it.hasNext()) {
-            final BasePermission bp = it.next();
-            if (bp.getSourcePackageSetting() == null) {
-                // We may not yet have parsed the package, so just see if
-                // we still know about its settings.
-                bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
-                        + " from package " + bp.getSourcePackageName());
-                it.remove();
-            } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
-                if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
-                    Slog.i(TAG, "Removing old permission tree: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
-                    flags |= UPDATE_PERMISSIONS_ALL;
-                    it.remove();
-                }
-            }
-        }
+        flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags);
 
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
-        final Iterator<BasePermission> permissionIter =
-                mPermissionManager.getPermissionIteratorTEMP();
-        while (permissionIter.hasNext()) {
-            final BasePermission bp = permissionIter.next();
-            if (bp.isDynamic()) {
-                bp.updateDynamicPermission(mSettings.mPermissionTrees);
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                // We may not yet have parsed the package, so just see if
-                // we still know about its settings.
-                bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                Slog.w(TAG, "Removing dangling permission: " + bp.getName()
-                        + " from package " + bp.getSourcePackageName());
-                permissionIter.remove();
-            } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
-                if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
-                    Slog.i(TAG, "Removing old permission: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
-                    flags |= UPDATE_PERMISSIONS_ALL;
-                    permissionIter.remove();
-                }
-            }
-        }
+        flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
         // Now update the permissions for all packages, in particular
@@ -12286,12 +12136,7 @@
 
             // Keep track of app op permissions.
             if (bp.isAppOp()) {
-                ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
-                if (pkgs == null) {
-                    pkgs = new ArraySet<>();
-                    mAppOpPermissionPackages.put(perm, pkgs);
-                }
-                pkgs.add(pkg.packageName);
+                mSettings.addAppOpPackage(perm, pkg.packageName);
             }
 
             if (bp.isNormal()) {
@@ -21238,7 +21083,7 @@
         // permissions, ensure permissions are updated. Beware of dragons if you
         // try optimizing this.
         synchronized (mPackages) {
-            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
+            updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
                     UPDATE_PERMISSIONS_ALL);
         }
 
@@ -21783,22 +21628,6 @@
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
-                if (packageName == null && permissionNames == null) {
-                    for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
-                        if (iperm == 0) {
-                            if (dumpState.onTitlePrinted())
-                                pw.println();
-                            pw.println("AppOp Permissions:");
-                        }
-                        pw.print("  AppOp Permission ");
-                        pw.print(mAppOpPermissionPackages.keyAt(iperm));
-                        pw.println(":");
-                        ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
-                        for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
-                            pw.print("    "); pw.println(pkgs.valueAt(ipkg));
-                        }
-                    }
-                }
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
@@ -22302,7 +22131,7 @@
                         + mSdkVersion + "; regranting permissions for " + volumeUuid);
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, volumeUuid, updateFlags);
+            updatePermissionsLocked(null, null, volumeUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -23310,15 +23139,15 @@
     void onNewUserCreated(final int userId) {
         synchronized(mPackages) {
             mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
-        }
-        // If permission review for legacy apps is required, we represent
-        // dagerous permissions for such apps as always granted runtime
-        // permissions to keep per user flag state whether review is needed.
-        // Hence, if a new user is added we have to propagate dangerous
-        // permission grants for these legacy apps.
-        if (mPermissionReviewRequired) {
-            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
-                    | UPDATE_PERMISSIONS_REPLACE_ALL);
+            // If permission review for legacy apps is required, we represent
+            // dagerous permissions for such apps as always granted runtime
+            // permissions to keep per user flag state whether review is needed.
+            // Hence, if a new user is added we have to propagate dangerous
+            // permission grants for these legacy apps.
+            if (mPermissionReviewRequired) {
+                updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                        | UPDATE_PERMISSIONS_REPLACE_ALL);
+            }
         }
     }
 
@@ -23762,12 +23591,12 @@
         }
 
         @Override
-        public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
+        public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) {
             synchronized (mPackages) {
-                return BasePermission.enforcePermissionTreeLP(
-                        mSettings.mPermissionTrees, permName, callingUid);
+                return mPermissionGroups.get(groupName);
             }
         }
+
         @Override
         public boolean isInstantApp(String packageName, int userId) {
             return PackageManagerService.this.isInstantApp(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0084411..56595c9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -378,10 +378,6 @@
     private final ArrayMap<Long, Integer> mKeySetRefs =
             new ArrayMap<Long, Integer>();
 
-    // Mapping from permission tree names to info about them.
-    final ArrayMap<String, BasePermission> mPermissionTrees =
-            new ArrayMap<String, BasePermission>();
-
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
@@ -416,7 +412,7 @@
 
     public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
     /** Settings and other information about permissions */
-    private final PermissionSettings mPermissions;
+    final PermissionSettings mPermissions;
 
     Settings(PermissionSettings permissions, Object lock) {
         this(Environment.getDataDirectory(), permissions, lock);
@@ -622,6 +618,10 @@
         return null;
     }
 
+    void addAppOpPackage(String permName, String packageName) {
+        mPermissions.addAppOpPackage(permName, packageName);
+    }
+
     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
         SharedUserSetting s = mSharedUsers.get(name);
         if (s != null) {
@@ -666,13 +666,6 @@
     }
 
     /**
-     * Transfers ownership of permissions from one package to another.
-     */
-    void transferPermissionsLPw(String origPackageName, String newPackageName) {
-        mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
-    }
-
-    /**
      * Creates a new {@code PackageSetting} object.
      * Use this method instead of the constructor to ensure a settings object is created
      * with the correct base.
@@ -2496,9 +2489,7 @@
             }
 
             serializer.startTag(null, "permission-trees");
-            for (BasePermission bp : mPermissionTrees.values()) {
-                writePermissionLPr(serializer, bp);
-            }
+            mPermissions.writePermissionTrees(serializer);
             serializer.endTag(null, "permission-trees");
 
             serializer.startTag(null, "permissions");
@@ -3042,7 +3033,7 @@
                 } else if (tagName.equals("permissions")) {
                     mPermissions.readPermissions(parser);
                 } else if (tagName.equals("permission-trees")) {
-                    PermissionSettings.readPermissions(mPermissionTrees, parser);
+                    mPermissions.readPermissionTrees(parser);
                 } else if (tagName.equals("shared-user")) {
                     readSharedUserLPw(parser);
                 } else if (tagName.equals("preferred-packages")) {
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 4f5d156..815f885 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -21,6 +21,8 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.drawable.Icon;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
@@ -165,7 +167,13 @@
 
         // Compress it and enqueue to the requests.
         final byte[] bytes;
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
+            // compress() triggers a slow call, but in this case it's needed to save RAM and also
+            // the target bitmap is of an icon size, so let's just permit it.
+            StrictMode.setThreadPolicy(new ThreadPolicy.Builder(oldPolicy)
+                    .permitCustomSlowCalls()
+                    .build());
             final Bitmap shrunk = mService.shrinkBitmap(original, maxDimension);
             try {
                 try (final ByteArrayOutputStream out = new ByteArrayOutputStream(64 * 1024)) {
@@ -184,6 +192,8 @@
         } catch (IOException | RuntimeException | OutOfMemoryError e) {
             Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
             return;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
         }
 
         shortcut.addFlags(
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index f922ad1..cedf476 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -83,11 +83,16 @@
         return mOwnerUserId;
     }
 
+    @Override
+    protected boolean canRestoreAnyVersion() {
+        // Launcher's pinned shortcuts can be restored to an older version.
+        return true;
+    }
+
     /**
      * Called when the new package can't receive the backup, due to signature or version mismatch.
      */
-    @Override
-    protected void onRestoreBlocked() {
+    private void onRestoreBlocked() {
         final ArrayList<PackageWithUser> pinnedPackages =
                 new ArrayList<>(mPinnedShortcuts.keySet());
         mPinnedShortcuts.clear();
@@ -101,15 +106,21 @@
     }
 
     @Override
-    protected void onRestored() {
-        // Nothing to do.
+    protected void onRestored(int restoreBlockReason) {
+        // For launcher, possible reasons here are DISABLED_REASON_SIGNATURE_MISMATCH or
+        // DISABLED_REASON_BACKUP_NOT_SUPPORTED.
+        // DISABLED_REASON_VERSION_LOWER will NOT happen because we don't check version
+        // code for launchers.
+        if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+            onRestoreBlocked();
+        }
     }
 
     /**
      * Pin the given shortcuts, replacing the current pinned ones.
      */
     public void pinShortcuts(@UserIdInt int packageUserId,
-            @NonNull String packageName, @NonNull List<String> ids) {
+            @NonNull String packageName, @NonNull List<String> ids, boolean forPinRequest) {
         final ShortcutPackage packageShortcuts =
                 mShortcutUser.getPackageShortcutsIfExists(packageName);
         if (packageShortcuts == null) {
@@ -124,8 +135,12 @@
         } else {
             final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
 
-            // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
-            // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
+            // Actually pin shortcuts.
+            // This logic here is to make sure a launcher cannot pin a shortcut that is floating
+            // (i.e. not dynamic nor manifest but is pinned) and pinned by another launcher.
+            // In this case, technically the shortcut doesn't exist to this launcher, so it can't
+            // pin it.
+            // (Maybe unnecessarily strict...)
 
             final ArraySet<String> newSet = new ArraySet<>();
 
@@ -135,8 +150,10 @@
                 if (si == null) {
                     continue;
                 }
-                if (si.isDynamic() || si.isManifestShortcut()
-                        || (prevSet != null && prevSet.contains(id))) {
+                if (si.isDynamic()
+                        || si.isManifestShortcut()
+                        || (prevSet != null && prevSet.contains(id))
+                        || forPinRequest) {
                     newSet.add(id);
                 }
             }
@@ -155,7 +172,7 @@
     }
 
     /**
-     * Return true if the given shortcut is pinned by this launcher.
+     * Return true if the given shortcut is pinned by this launcher.<code></code>
      */
     public boolean hasPinned(ShortcutInfo shortcut) {
         final ArraySet<String> pinned =
@@ -164,10 +181,10 @@
     }
 
     /**
-     * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List)}
+     * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List, boolean)}
      */
     public void addPinnedShortcut(@NonNull String packageName, @UserIdInt int packageUserId,
-            String id) {
+            String id, boolean forPinRequest) {
         final ArraySet<String> pinnedSet = getPinnedShortcutIds(packageName, packageUserId);
         final ArrayList<String> pinnedList;
         if (pinnedSet != null) {
@@ -178,21 +195,21 @@
         }
         pinnedList.add(id);
 
-        pinShortcuts(packageUserId, packageName, pinnedList);
+        pinShortcuts(packageUserId, packageName, pinnedList, forPinRequest);
     }
 
     boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
         return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
     }
 
-    public void ensureVersionInfo() {
+    public void ensurePackageInfo() {
         final PackageInfo pi = mShortcutUser.mService.getPackageInfoWithSignatures(
                 getPackageName(), getPackageUserId());
         if (pi == null) {
             Slog.w(TAG, "Package not found: " + getPackageName());
             return;
         }
-        getPackageInfo().updateVersionInfo(pi);
+        getPackageInfo().updateFromPackageInfo(pi);
     }
 
     /**
@@ -201,6 +218,10 @@
     @Override
     public void saveToXml(XmlSerializer out, boolean forBackup)
             throws IOException {
+        if (forBackup && !getPackageInfo().isBackupAllowed()) {
+            // If an launcher app doesn't support backup&restore, then nothing to do.
+            return;
+        }
         final int size = mPinnedShortcuts.size();
         if (size == 0) {
             return; // Nothing to write.
@@ -209,7 +230,7 @@
         out.startTag(null, TAG_ROOT);
         ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
         ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
-        getPackageInfo().saveToXml(out);
+        getPackageInfo().saveToXml(out, forBackup);
 
         for (int i = 0; i < size; i++) {
             final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 6fc1e73..a4bec1d 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -84,6 +84,7 @@
     private static final String ATTR_DISABLED_MESSAGE = "dmessage";
     private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
     private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
+    private static final String ATTR_DISABLED_REASON = "disabled-reason";
     private static final String ATTR_INTENT_LEGACY = "intent";
     private static final String ATTR_INTENT_NO_EXTRA = "intent-base";
     private static final String ATTR_RANK = "rank";
@@ -156,13 +157,25 @@
     }
 
     @Override
-    protected void onRestoreBlocked() {
-        // Can't restore due to version/signature mismatch.  Remove all shortcuts.
-        mShortcuts.clear();
+    protected boolean canRestoreAnyVersion() {
+        return false;
     }
 
     @Override
-    protected void onRestored() {
+    protected void onRestored(int restoreBlockReason) {
+        // Shortcuts have been restored.
+        // - Unshadow all shortcuts.
+        // - Set disabled reason.
+        // - Disable if needed.
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            ShortcutInfo si = mShortcuts.valueAt(i);
+            si.clearFlags(ShortcutInfo.FLAG_SHADOW);
+
+            si.setDisabledReason(restoreBlockReason);
+            if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+                si.addFlags(ShortcutInfo.FLAG_DISABLED);
+            }
+        }
         // Because some launchers may not have been restored (e.g. allowBackup=false),
         // we need to re-calculate the pinned shortcuts.
         refreshPinnedFlags();
@@ -176,31 +189,47 @@
         return mShortcuts.get(id);
     }
 
-    private void ensureNotImmutable(@Nullable ShortcutInfo shortcut) {
-        if (shortcut != null && shortcut.isImmutable()) {
+    public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
+        ShortcutInfo si = findShortcutById(id);
+        return si != null && !si.isVisibleToPublisher();
+    }
+
+    public boolean isShortcutExistsAndVisibleToPublisher(String id) {
+        ShortcutInfo si = findShortcutById(id);
+        return si != null && si.isVisibleToPublisher();
+    }
+
+    private void ensureNotImmutable(@Nullable ShortcutInfo shortcut, boolean ignoreInvisible) {
+        if (shortcut != null && shortcut.isImmutable()
+                && (!ignoreInvisible || shortcut.isVisibleToPublisher())) {
             throw new IllegalArgumentException(
                     "Manifest shortcut ID=" + shortcut.getId()
                             + " may not be manipulated via APIs");
         }
     }
 
-    public void ensureNotImmutable(@NonNull String id) {
-        ensureNotImmutable(mShortcuts.get(id));
+    public void ensureNotImmutable(@NonNull String id, boolean ignoreInvisible) {
+        ensureNotImmutable(mShortcuts.get(id), ignoreInvisible);
     }
 
-    public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds) {
+    public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds,
+            boolean ignoreInvisible) {
         for (int i = shortcutIds.size() - 1; i >= 0; i--) {
-            ensureNotImmutable(shortcutIds.get(i));
+            ensureNotImmutable(shortcutIds.get(i), ignoreInvisible);
         }
     }
 
-    public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts) {
+    public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts,
+            boolean ignoreInvisible) {
         for (int i = shortcuts.size() - 1; i >= 0; i--) {
-            ensureNotImmutable(shortcuts.get(i).getId());
+            ensureNotImmutable(shortcuts.get(i).getId(), ignoreInvisible);
         }
     }
 
-    private ShortcutInfo deleteShortcutInner(@NonNull String id) {
+    /**
+     * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
+     */
+    private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
         final ShortcutInfo shortcut = mShortcuts.remove(id);
         if (shortcut != null) {
             mShortcutUser.mService.removeIconLocked(shortcut);
@@ -210,10 +239,14 @@
         return shortcut;
     }
 
-    private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
+    /**
+     * Force replace a shortcut. If there's already a shortcut with the same ID, it'll be removed,
+     * even if it's invisible.
+     */
+    private void forceReplaceShortcutInner(@NonNull ShortcutInfo newShortcut) {
         final ShortcutService s = mShortcutUser.mService;
 
-        deleteShortcutInner(newShortcut.getId());
+        forceDeleteShortcutInner(newShortcut.getId());
 
         // Extract Icon and update the icon res ID and the bitmap path.
         s.saveIconAndFixUpShortcutLocked(newShortcut);
@@ -222,11 +255,12 @@
     }
 
     /**
-     * Add a shortcut, or update one with the same ID, with taking over existing flags.
+     * Add a shortcut. If there's already a one with the same ID, it'll be removed, even if it's
+     * invisible.
      *
      * It checks the max number of dynamic shortcuts.
      */
-    public void addOrUpdateDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+    public void addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
 
         Preconditions.checkArgument(newShortcut.isEnabled(),
                 "add/setDynamicShortcuts() cannot publish disabled shortcuts");
@@ -242,7 +276,7 @@
         } else {
             // It's an update case.
             // Make sure the target is updatable. (i.e. should be mutable.)
-            oldShortcut.ensureUpdatableWith(newShortcut);
+            oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
 
             wasPinned = oldShortcut.isPinned();
         }
@@ -252,7 +286,7 @@
             newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
         }
 
-        addShortcutInner(newShortcut);
+        forceReplaceShortcutInner(newShortcut);
     }
 
     /**
@@ -273,7 +307,7 @@
         }
         if (removeList != null) {
             for (int i = removeList.size() - 1; i >= 0; i--) {
-                deleteShortcutInner(removeList.get(i));
+                forceDeleteShortcutInner(removeList.get(i));
             }
         }
     }
@@ -281,13 +315,13 @@
     /**
      * Remove all dynamic shortcuts.
      */
-    public void deleteAllDynamicShortcuts() {
+    public void deleteAllDynamicShortcuts(boolean ignoreInvisible) {
         final long now = mShortcutUser.mService.injectCurrentTimeMillis();
 
         boolean changed = false;
         for (int i = mShortcuts.size() - 1; i >= 0; i--) {
             final ShortcutInfo si = mShortcuts.valueAt(i);
-            if (si.isDynamic()) {
+            if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
                 changed = true;
 
                 si.setTimestamp(now);
@@ -307,9 +341,10 @@
      * @return true if it's actually removed because it wasn't pinned, or false if it's still
      * pinned.
      */
-    public boolean deleteDynamicWithId(@NonNull String shortcutId) {
+    public boolean deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
         final ShortcutInfo removed = deleteOrDisableWithId(
-                shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
+                shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
+                ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
         return removed == null;
     }
 
@@ -320,9 +355,11 @@
      * @return true if it's actually removed because it wasn't pinned, or false if it's still
      * pinned.
      */
-    private boolean disableDynamicWithId(@NonNull String shortcutId) {
+    private boolean disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
+            int disabledReason) {
         final ShortcutInfo disabled = deleteOrDisableWithId(
-                shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false);
+                shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false, ignoreInvisible,
+                disabledReason);
         return disabled == null;
     }
 
@@ -331,9 +368,10 @@
      * is pinned, it'll remain as a pinned shortcut but will be disabled.
      */
     public void disableWithId(@NonNull String shortcutId, String disabledMessage,
-            int disabledMessageResId, boolean overrideImmutable) {
+            int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
+            int disabledReason) {
         final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
-                overrideImmutable);
+                overrideImmutable, ignoreInvisible, disabledReason);
 
         if (disabled != null) {
             if (disabledMessage != null) {
@@ -348,14 +386,18 @@
 
     @Nullable
     private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
-            boolean overrideImmutable) {
+            boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
+        Preconditions.checkState(
+                (disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
+                "disable and disabledReason disagree: " + disable + " vs " + disabledReason);
         final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
 
-        if (oldShortcut == null || !oldShortcut.isEnabled()) {
+        if (oldShortcut == null || !oldShortcut.isEnabled()
+                && (ignoreInvisible && !oldShortcut.isVisibleToPublisher())) {
             return null; // Doesn't exist or already disabled.
         }
         if (!overrideImmutable) {
-            ensureNotImmutable(oldShortcut);
+            ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
         }
         if (oldShortcut.isPinned()) {
 
@@ -363,6 +405,10 @@
             oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
             if (disable) {
                 oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
+                // Do not overwrite the disabled reason if one is alreay set.
+                if (oldShortcut.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+                    oldShortcut.setDisabledReason(disabledReason);
+                }
             }
             oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
 
@@ -373,7 +419,7 @@
 
             return oldShortcut;
         } else {
-            deleteShortcutInner(shortcutId);
+            forceDeleteShortcutInner(shortcutId);
             return null;
         }
     }
@@ -381,11 +427,25 @@
     public void enableWithId(@NonNull String shortcutId) {
         final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
         if (shortcut != null) {
-            ensureNotImmutable(shortcut);
+            ensureNotImmutable(shortcut, /*ignoreInvisible=*/ true);
             shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
+            shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
         }
     }
 
+    public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
+        final ShortcutInfo source = mShortcuts.get(shortcut.getId());
+        Preconditions.checkNotNull(source);
+
+        mShortcutUser.mService.validateShortcutForPinRequest(shortcut);
+
+        shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+
+        forceReplaceShortcutInner(shortcut);
+
+        adjustRanks();
+    }
+
     /**
      * Called after a launcher updates the pinned set.  For each shortcut in this package,
      * set FLAG_PINNED if any launcher has pinned it.  Otherwise, clear it.
@@ -693,7 +753,27 @@
                     getPackageInfo().getVersionCode(), pi.versionCode));
         }
 
-        getPackageInfo().updateVersionInfo(pi);
+        getPackageInfo().updateFromPackageInfo(pi);
+        final int newVersionCode = getPackageInfo().getVersionCode();
+
+        // See if there are any shortcuts that were prevented restoring because the app was of a
+        // lower version, and re-enable them.
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            final ShortcutInfo si = mShortcuts.valueAt(i);
+            if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+                continue;
+            }
+            if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
+                if (ShortcutService.DEBUG) {
+                    Slog.d(TAG, String.format("Shortcut %s require version %s, still not restored.",
+                            si.getId(), getPackageInfo().getBackupSourceVersionCode()));
+                }
+                continue;
+            }
+            Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
+            si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+            si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+        }
 
         // For existing shortcuts, update timestamps if they have any resources.
         // Also check if shortcuts' activities are still main activities.  Otherwise, disable them.
@@ -713,7 +793,8 @@
                         Slog.w(TAG, String.format(
                                 "%s is no longer main activity. Disabling shorcut %s.",
                                 getPackageName(), si.getId()));
-                        if (disableDynamicWithId(si.getId())) {
+                        if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
+                                ShortcutInfo.DISABLED_REASON_APP_CHANGED)) {
                             continue; // Actually removed.
                         }
                         // Still pinned, so fall-through and possibly update the resources.
@@ -809,7 +890,7 @@
 
                 // Note even if enabled=false, we still need to update all fields, so do it
                 // regardless.
-                addShortcutInner(newShortcut); // This will clean up the old one too.
+                forceReplaceShortcutInner(newShortcut); // This will clean up the old one too.
 
                 if (!newDisabled && toDisableList != null) {
                     // Still alive, don't remove.
@@ -831,7 +912,8 @@
                 final String id = toDisableList.valueAt(i);
 
                 disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
-                        /* overrideImmutable=*/ true);
+                        /* overrideImmutable=*/ true, /*ignoreInvisible=*/ false,
+                        ShortcutInfo.DISABLED_REASON_APP_CHANGED);
             }
             removeOrphans();
         }
@@ -869,7 +951,7 @@
                     service.wtf("Found manifest shortcuts in excess list.");
                     continue;
                 }
-                deleteDynamicWithId(shortcut.getId());
+                deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
             }
         }
 
@@ -1075,7 +1157,7 @@
         if (ret != 0) {
             return ret;
         }
-        // If they're stil tie, just sort by their IDs.
+        // If they're still tie, just sort by their IDs.
         // This may happen with updateShortcuts() -- see
         // the testUpdateShortcuts_noManifestShortcuts() test.
         return a.getId().compareTo(b.getId());
@@ -1257,25 +1339,34 @@
         ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
         ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
         ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
-        getPackageInfo().saveToXml(out);
+        getPackageInfo().saveToXml(out, forBackup);
 
         for (int j = 0; j < size; j++) {
-            saveShortcut(out, mShortcuts.valueAt(j), forBackup);
+            saveShortcut(out, mShortcuts.valueAt(j), forBackup,
+                    getPackageInfo().isBackupAllowed());
         }
 
         out.endTag(null, TAG_ROOT);
     }
 
-    private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
+    private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup,
+            boolean appSupportsBackup)
             throws IOException, XmlPullParserException {
 
         final ShortcutService s = mShortcutUser.mService;
 
         if (forBackup) {
             if (!(si.isPinned() && si.isEnabled())) {
-                return; // We only backup pinned shortcuts that are enabled.
+                // We only backup pinned shortcuts that are enabled.
+                // Note, this means, shortcuts that are restored but are blocked restore, e.g. due
+                // to a lower version code, will not be ported to a new device.
+                return;
             }
         }
+        final boolean shouldBackupDetails =
+                !forBackup // It's not backup
+                || appSupportsBackup; // Or, it's a backup and app supports backup.
+
         // Note: at this point no shortcuts should have bitmaps pending save, but if they do,
         // just remove the bitmap.
         if (si.isIconPendingSave()) {
@@ -1292,20 +1383,31 @@
         ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
         ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
         ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
-        ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
-        ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
-                si.getDisabledMessageResourceId());
-        ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
-                si.getDisabledMessageResName());
+        if (shouldBackupDetails) {
+            ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
+            ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
+                    si.getDisabledMessageResourceId());
+            ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
+                    si.getDisabledMessageResName());
+        }
+        ShortcutService.writeAttr(out, ATTR_DISABLED_REASON, si.getDisabledReason());
         ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
                 si.getLastChangedTimestamp());
         if (forBackup) {
             // Don't write icon information.  Also drop the dynamic flag.
-            ShortcutService.writeAttr(out, ATTR_FLAGS,
-                    si.getFlags() &
-                            ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
+
+            int flags = si.getFlags() &
+                    ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
                             | ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE
-                            | ShortcutInfo.FLAG_DYNAMIC));
+                            | ShortcutInfo.FLAG_DYNAMIC);
+            ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
+
+            // Set the publisher version code at every backup.
+            final int packageVersionCode = getPackageInfo().getVersionCode();
+            if (packageVersionCode == 0) {
+                s.wtf("Package version code should be available at this point.");
+                // However, 0 is a valid version code, so we just go ahead with it...
+            }
         } else {
             // When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored
             // as dynamic.
@@ -1317,26 +1419,28 @@
             ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
         }
 
-        {
-            final Set<String> cat = si.getCategories();
-            if (cat != null && cat.size() > 0) {
-                out.startTag(null, TAG_CATEGORIES);
-                XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
-                        NAME_CATEGORIES, out);
-                out.endTag(null, TAG_CATEGORIES);
+        if (shouldBackupDetails) {
+            {
+                final Set<String> cat = si.getCategories();
+                if (cat != null && cat.size() > 0) {
+                    out.startTag(null, TAG_CATEGORIES);
+                    XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
+                            NAME_CATEGORIES, out);
+                    out.endTag(null, TAG_CATEGORIES);
+                }
             }
-        }
-        final Intent[] intentsNoExtras = si.getIntentsNoExtras();
-        final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
-        final int numIntents = intentsNoExtras.length;
-        for (int i = 0; i < numIntents; i++) {
-            out.startTag(null, TAG_INTENT);
-            ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
-            ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
-            out.endTag(null, TAG_INTENT);
-        }
+            final Intent[] intentsNoExtras = si.getIntentsNoExtras();
+            final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
+            final int numIntents = intentsNoExtras.length;
+            for (int i = 0; i < numIntents; i++) {
+                out.startTag(null, TAG_INTENT);
+                ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
+                ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
+                out.endTag(null, TAG_INTENT);
+            }
 
-        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+            ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+        }
 
         out.endTag(null, TAG_SHORTCUT);
     }
@@ -1356,6 +1460,7 @@
         ret.mLastResetTime =
                 ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
+
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1369,10 +1474,11 @@
                 switch (tag) {
                     case ShortcutPackageInfo.TAG_ROOT:
                         ret.getPackageInfo().loadFromXml(parser, fromBackup);
+
                         continue;
                     case TAG_SHORTCUT:
                         final ShortcutInfo si = parseShortcut(parser, packageName,
-                                shortcutUser.getUserId());
+                                shortcutUser.getUserId(), fromBackup);
 
                         // Don't use addShortcut(), we don't need to save the icon.
                         ret.mShortcuts.put(si.getId(), si);
@@ -1385,7 +1491,8 @@
     }
 
     private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
-            @UserIdInt int userId) throws IOException, XmlPullParserException {
+            @UserIdInt int userId, boolean fromBackup)
+            throws IOException, XmlPullParserException {
         String id;
         ComponentName activityComponent;
         // Icon icon;
@@ -1398,6 +1505,7 @@
         String disabledMessage;
         int disabledMessageResId;
         String disabledMessageResName;
+        int disabledReason;
         Intent intentLegacy;
         PersistableBundle intentPersistableExtrasLegacy = null;
         ArrayList<Intent> intents = new ArrayList<>();
@@ -1408,6 +1516,7 @@
         int iconResId;
         String iconResName;
         String bitmapPath;
+        int backupVersionCode;
         ArraySet<String> categories = null;
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
@@ -1424,6 +1533,7 @@
                 ATTR_DISABLED_MESSAGE_RES_ID);
         disabledMessageResName = ShortcutService.parseStringAttribute(parser,
                 ATTR_DISABLED_MESSAGE_RES_NAME);
+        disabledReason = ShortcutService.parseIntAttribute(parser, ATTR_DISABLED_REASON);
         intentLegacy = ShortcutService.parseIntentAttributeNoDefault(parser, ATTR_INTENT_LEGACY);
         rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
         lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
@@ -1480,6 +1590,19 @@
             intents.add(intentLegacy);
         }
 
+
+        if ((disabledReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
+                && ((flags & ShortcutInfo.FLAG_DISABLED) != 0)) {
+            // We didn't used to have the disabled reason, so if a shortcut is disabled
+            // and has no reason, we assume it was disabled by publisher.
+            disabledReason = ShortcutInfo.DISABLED_REASON_BY_APP;
+        }
+
+        // All restored shortcuts are initially "shadow".
+        if (fromBackup) {
+            flags |= ShortcutInfo.FLAG_SHADOW;
+        }
+
         return new ShortcutInfo(
                 userId, id, packageName, activityComponent, /* icon =*/ null,
                 title, titleResId, titleResName, text, textResId, textResName,
@@ -1487,7 +1610,7 @@
                 categories,
                 intents.toArray(new Intent[intents.size()]),
                 rank, extras, lastChangedTimestamp, flags,
-                iconResId, iconResName, bitmapPath);
+                iconResId, iconResName, bitmapPath, disabledReason);
     }
 
     private static Intent parseIntent(XmlPullParser parser)
@@ -1602,6 +1725,20 @@
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                         + " has both resource and bitmap icons");
             }
+            if (si.isEnabled()
+                    != (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)) {
+                failed = true;
+                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+                        + " isEnabled() and getDisabledReason() disagree: "
+                        + si.isEnabled() + " vs " + si.getDisabledReason());
+            }
+            if ((si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+                    && (getPackageInfo().getBackupSourceVersionCode()
+                    == ShortcutInfo.VERSION_CODE_UNKNOWN)) {
+                failed = true;
+                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+                        + " RESTORED_VERSION_LOWER with no backup source version code.");
+            }
             if (s.isDummyMainActivity(si.getActivity())) {
                 failed = true;
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index e5a2f5a..3a9bbc8 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.pm.PackageInfo;
+import android.content.pm.ShortcutInfo;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -45,32 +46,45 @@
     static final String TAG_ROOT = "package-info";
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
+    private static final String ATTR_BACKUP_SOURCE_VERSION = "bk_src_version";
+    private static final String ATTR_BACKUP_ALLOWED = "allow-backup";
+    private static final String ATTR_BACKUP_SOURCE_BACKUP_ALLOWED = "bk_src_backup-allowed";
     private static final String ATTR_SHADOW = "shadow";
 
     private static final String TAG_SIGNATURE = "signature";
     private static final String ATTR_SIGNATURE_HASH = "hash";
 
-    private static final int VERSION_UNKNOWN = -1;
-
     /**
      * When true, this package information was restored from the previous device, and the app hasn't
      * been installed yet.
      */
     private boolean mIsShadow;
-    private int mVersionCode = VERSION_UNKNOWN;
+    private int mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+    private int mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
     private long mLastUpdateTime;
     private ArrayList<byte[]> mSigHashes;
 
+    // mBackupAllowed didn't used to be parsisted, so we don't restore it from a file.
+    // mBackupAllowed will always start with false, and will have been updated before making a
+    // backup next time, which works file.
+    // We just don't want to print an uninitialzied mBackupAlldowed value on dumpsys, so
+    // we use this boolean to control dumpsys.
+    private boolean mBackupAllowedInitialized;
+    private boolean mBackupAllowed;
+    private boolean mBackupSourceBackupAllowed;
+
     private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
             ArrayList<byte[]> sigHashes, boolean isShadow) {
         mVersionCode = versionCode;
         mLastUpdateTime = lastUpdateTime;
         mIsShadow = isShadow;
         mSigHashes = sigHashes;
+        mBackupAllowed = false; // By default, we assume false.
+        mBackupSourceBackupAllowed = false;
     }
 
     public static ShortcutPackageInfo newEmpty() {
-        return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
+        return new ShortcutPackageInfo(ShortcutInfo.VERSION_CODE_UNKNOWN, /* last update time =*/ 0,
                 new ArrayList<>(0), /* isShadow */ false);
     }
 
@@ -86,15 +100,33 @@
         return mVersionCode;
     }
 
+    public int getBackupSourceVersionCode() {
+        return mBackupSourceVersionCode;
+    }
+
+    @VisibleForTesting
+    public boolean isBackupSourceBackupAllowed() {
+        return mBackupSourceBackupAllowed;
+    }
+
     public long getLastUpdateTime() {
         return mLastUpdateTime;
     }
 
-    /** Set {@link #mVersionCode} and {@link #mLastUpdateTime} from a {@link PackageInfo}. */
-    public void updateVersionInfo(@NonNull PackageInfo pi) {
+    public boolean isBackupAllowed() {
+        return mBackupAllowed;
+    }
+
+    /**
+     * Set {@link #mVersionCode}, {@link #mLastUpdateTime} and {@link #mBackupAllowed}
+     * from a {@link PackageInfo}.
+     */
+    public void updateFromPackageInfo(@NonNull PackageInfo pi) {
         if (pi != null) {
             mVersionCode = pi.versionCode;
             mLastUpdateTime = pi.lastUpdateTime;
+            mBackupAllowed = ShortcutService.shouldBackupApp(pi);
+            mBackupAllowedInitialized = true;
         }
     }
 
@@ -102,23 +134,24 @@
         return mSigHashes.size() > 0;
     }
 
-    public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
-        if (!s.shouldBackupApp(target)) {
-            // "allowBackup" was true when backed up, but now false.
-            Slog.w(TAG, "Can't restore: package no longer allows backup");
-            return false;
+    //@DisabledReason
+    public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) {
+        if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) {
+            Slog.w(TAG, "Can't restore: Package signature mismatch");
+            return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
         }
-        if (target.versionCode < mVersionCode) {
+        if (!ShortcutService.shouldBackupApp(currentPackage) || !mBackupSourceBackupAllowed) {
+            // "allowBackup" was true when backed up, but now false.
+            Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup");
+            return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
+        }
+        if (!anyVersionOkay && (currentPackage.versionCode < mBackupSourceVersionCode)) {
             Slog.w(TAG, String.format(
                     "Can't restore: package current version %d < backed up version %d",
-                    target.versionCode, mVersionCode));
-            return false;
+                    currentPackage.versionCode, mBackupSourceVersionCode));
+            return ShortcutInfo.DISABLED_REASON_VERSION_LOWER;
         }
-        if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
-            Slog.w(TAG, "Can't restore: Package signature mismatch");
-            return false;
-        }
-        return true;
+        return ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
     }
 
     @VisibleForTesting
@@ -132,6 +165,8 @@
         final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
                 BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
 
+        ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi);
+        ret.mBackupSourceVersionCode = pi.versionCode;
         return ret;
     }
 
@@ -151,13 +186,19 @@
         mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
     }
 
-    public void saveToXml(XmlSerializer out) throws IOException {
+    public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException {
 
         out.startTag(null, TAG_ROOT);
 
         ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
         ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
         ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
+        ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED, mBackupAllowed);
+
+        ShortcutService.writeAttr(out, ATTR_BACKUP_SOURCE_VERSION, mBackupSourceVersionCode);
+        ShortcutService.writeAttr(out,
+                ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, mBackupSourceBackupAllowed);
+
 
         for (int i = 0; i < mSigHashes.size(); i++) {
             out.startTag(null, TAG_SIGNATURE);
@@ -171,7 +212,9 @@
     public void loadFromXml(XmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
 
-        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
+        // Don't use the version code from the backup file.
+        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION,
+                ShortcutInfo.VERSION_CODE_UNKNOWN);
 
         final long lastUpdateTime = ShortcutService.parseLongAttribute(
                 parser, ATTR_LAST_UPDATE_TIME);
@@ -180,6 +223,20 @@
         final boolean shadow =
                 fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
 
+        // We didn't used to save these attributes, and all backed up shortcuts were from
+        // apps that support backups, so the default values take this fact into consideration.
+        final int backupSourceVersion = ShortcutService.parseIntAttribute(parser,
+                ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN);
+
+        // Note the only time these "true" default value is used is when restoring from an old
+        // build that didn't save ATTR_BACKUP_ALLOWED, and that means all the data included in
+        // a backup file were from apps that support backup, so we can just use "true" as the
+        // default.
+        final boolean backupAllowed = ShortcutService.parseBooleanAttribute(
+                parser, ATTR_BACKUP_ALLOWED, true);
+        final boolean backupSourceBackupAllowed = ShortcutService.parseBooleanAttribute(
+                parser, ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, true);
+
         final ArrayList<byte[]> hashes = new ArrayList<>();
 
         final int outerDepth = parser.getDepth();
@@ -207,11 +264,28 @@
             ShortcutService.warnForInvalidTag(depth, tag);
         }
 
-        // Successfully loaded; replace the feilds.
-        mVersionCode = versionCode;
+        // Successfully loaded; replace the fields.
+        if (fromBackup) {
+            mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+            mBackupSourceVersionCode = versionCode;
+            mBackupSourceBackupAllowed = backupAllowed;
+        } else {
+            mVersionCode = versionCode;
+            mBackupSourceVersionCode = backupSourceVersion;
+            mBackupSourceBackupAllowed = backupSourceBackupAllowed;
+        }
         mLastUpdateTime = lastUpdateTime;
         mIsShadow = shadow;
         mSigHashes = hashes;
+
+        // Note we don't restore it from the file because it didn't used to be saved.
+        // We always start by assuming backup is disabled for the current package,
+        // and this field will have been updated before we actually create a backup, at the same
+        // time when we update the version code.
+        // Until then, the value of mBackupAllowed shouldn't matter, but we don't want to print
+        // a false flag on dumpsys, so set mBackupAllowedInitialized to false.
+        mBackupAllowed = false;
+        mBackupAllowedInitialized = false;
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -223,6 +297,7 @@
         pw.print(prefix);
         pw.print("  IsShadow: ");
         pw.print(mIsShadow);
+        pw.print(mIsShadow ? " (not installed)" : " (installed)");
         pw.println();
 
         pw.print(prefix);
@@ -230,6 +305,25 @@
         pw.print(mVersionCode);
         pw.println();
 
+        if (mBackupAllowedInitialized) {
+            pw.print(prefix);
+            pw.print("  Backup Allowed: ");
+            pw.print(mBackupAllowed);
+            pw.println();
+        }
+
+        if (mBackupSourceVersionCode != ShortcutInfo.VERSION_CODE_UNKNOWN) {
+            pw.print(prefix);
+            pw.print("  Backup source version: ");
+            pw.print(mBackupSourceVersionCode);
+            pw.println();
+
+            pw.print(prefix);
+            pw.print("  Backup source backup allowed: ");
+            pw.print(mBackupSourceBackupAllowed);
+            pw.println();
+        }
+
         pw.print(prefix);
         pw.print("  Last package update time: ");
         pw.print(mLastUpdateTime);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index e59d69f..97b7b59 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.content.pm.PackageInfo;
+import android.content.pm.ShortcutInfo;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
@@ -101,51 +102,42 @@
         final ShortcutService s = mShortcutUser.mService;
         if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
             if (ShortcutService.DEBUG) {
-                Slog.d(TAG, String.format("Package still not installed: %s user=%d",
+                Slog.d(TAG, String.format("Package still not installed: %s/u%d",
                         mPackageName, mPackageUserId));
             }
             return; // Not installed, no need to restore yet.
         }
-        boolean blockRestore = false;
-        if (!mPackageInfo.hasSignatures()) {
-            s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId
-                    + " but signatures not found in the restore data.");
-            blockRestore = true;
-        }
-        if (!blockRestore) {
-            final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
-            if (!mPackageInfo.canRestoreTo(s, pi)) {
-                // Package is now installed, but can't restore.  Let the subclass do the cleanup.
-                blockRestore = true;
-            }
-        }
-        if (blockRestore) {
-            onRestoreBlocked();
-        } else {
-            if (ShortcutService.DEBUG) {
-                Slog.d(TAG, String.format("Restored package: %s/%d on user %d", mPackageName,
-                        mPackageUserId, getOwnerUserId()));
-            }
+        int restoreBlockReason;
+        int currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
 
-            onRestored();
+        if (!mPackageInfo.hasSignatures()) {
+            s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId
+                    + " but signatures not found in the restore data.");
+            restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
+        } else {
+            final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
+            currentVersionCode = pi.versionCode;
+            restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion());
         }
 
+        if (ShortcutService.DEBUG) {
+            Slog.d(TAG, String.format("Restoring package: %s/u%d (version=%d) %s for u%d",
+                    mPackageName, mPackageUserId, currentVersionCode,
+                    ShortcutInfo.getDisabledReasonLabel(restoreBlockReason),
+                    getOwnerUserId()));
+        }
+
+        onRestored(restoreBlockReason);
+
         // Either way, it's no longer a shadow.
         mPackageInfo.setShadow(false);
 
         s.scheduleSaveUser(mPackageUserId);
     }
 
-    /**
-     * Called when the new package can't be restored because it has a lower version number
-     * or different signatures.
-     */
-    protected abstract void onRestoreBlocked();
+    protected abstract boolean canRestoreAnyVersion();
 
-    /**
-     * Called when the new package is successfully restored.
-     */
-    protected abstract void onRestored();
+    protected abstract void onRestored(int restoreBlockReason);
 
     public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 3cf4200..866c46c 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -337,6 +337,9 @@
                 (enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
                 | ShortcutInfo.FLAG_IMMUTABLE
                 | ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
+        final int disabledReason =
+                enabled ? ShortcutInfo.DISABLED_REASON_NOT_DISABLED
+                        : ShortcutInfo.DISABLED_REASON_BY_APP;
 
         // Note we don't need to set resource names here yet.  They'll be set when they're about
         // to be published.
@@ -363,6 +366,7 @@
                 flags,
                 iconResId,
                 null, // icon res name
-                null); // bitmap path
+                null, // bitmap path
+                disabledReason);
     }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 8a8128d..3e44de9 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -300,10 +300,12 @@
 
         final ShortcutInfo existing = ps.findShortcutById(inShortcut.getId());
         final boolean existsAlready = existing != null;
+        final boolean existingIsVisible = existsAlready && existing.isVisibleToPublisher();
 
         if (DEBUG) {
             Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage()
                     + " existsAlready=" + existsAlready
+                    + " existingIsVisible=" + existingIsVisible
                     + " shortcut=" + inShortcut.toInsecureString());
         }
 
@@ -378,7 +380,6 @@
         // manifest shortcut.)
         Preconditions.checkArgument(shortcutInfo.isEnabled(),
                 "Shortcut ID=" + shortcutInfo + " already exists but disabled.");
-
     }
 
     private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
@@ -463,7 +464,7 @@
             launcher.attemptToRestoreIfNeededAndSave();
             if (launcher.hasPinned(original)) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Shortcut " + original + " already pinned.");
+                    Slog.d(TAG, "Shortcut " + original + " already pinned.");                       // This too.
                 }
                 return true;
             }
@@ -497,7 +498,7 @@
                 if (original.getActivity() == null) {
                     original.setActivity(mService.getDummyMainActivity(appPackageName));
                 }
-                ps.addOrUpdateDynamicShortcut(original);
+                ps.addOrReplaceDynamicShortcut(original);
             }
 
             // Pin the shortcut.
@@ -505,13 +506,14 @@
                 Slog.d(TAG, "Pinning " + shortcutId);
             }
 
-            launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId);
+            launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
+                    /*forPinRequest=*/ true);
 
             if (current == null) {
                 if (DEBUG) {
                     Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
                 }
-                ps.deleteDynamicWithId(shortcutId);
+                ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
             }
 
             ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 27560c5f..9d86796 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -553,6 +553,9 @@
 
         public Lifecycle(Context context) {
             super(context);
+            if (DEBUG) {
+                Binder.LOG_RUNTIME_EXCEPTION = true;
+            }
             mService = new ShortcutService(context);
         }
 
@@ -738,6 +741,10 @@
         return parseLongAttribute(parser, attribute) == 1;
     }
 
+    static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) {
+        return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1;
+    }
+
     static int parseIntAttribute(XmlPullParser parser, String attribute) {
         return (int) parseLongAttribute(parser, attribute);
     }
@@ -835,6 +842,8 @@
     static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
         if (value) {
             writeAttr(out, name, "1");
+        } else {
+            writeAttr(out, name, "0");
         }
     }
 
@@ -1689,7 +1698,7 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
 
             fillInDefaultActivity(newShortcuts);
 
@@ -1709,12 +1718,12 @@
             }
 
             // First, remove all un-pinned; dynamic shortcuts
-            ps.deleteAllDynamicShortcuts();
+            ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
 
             // Then, add/update all.  We need to make sure to take over "pinned" flag.
             for (int i = 0; i < size; i++) {
                 final ShortcutInfo newShortcut = newShortcuts.get(i);
-                ps.addOrUpdateDynamicShortcut(newShortcut);
+                ps.addOrReplaceDynamicShortcut(newShortcut);
             }
 
             // Lastly, adjust the ranks.
@@ -1740,7 +1749,7 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
 
             // For update, don't fill in the default activity.  Having null activity means
             // "don't update the activity" here.
@@ -1761,7 +1770,9 @@
                 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
 
                 final ShortcutInfo target = ps.findShortcutById(source.getId());
-                if (target == null) {
+
+                // Invisible shortcuts can't be updated.
+                if (target == null || !target.isVisibleToPublisher()) {
                     continue;
                 }
 
@@ -1808,7 +1819,7 @@
     }
 
     @Override
-    public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+    public boolean  addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
@@ -1820,7 +1831,7 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
 
             fillInDefaultActivity(newShortcuts);
 
@@ -1845,7 +1856,7 @@
                 newShortcut.setRankChanged();
 
                 // Add it.
-                ps.addOrUpdateDynamicShortcut(newShortcut);
+                ps.addOrReplaceDynamicShortcut(newShortcut);
             }
 
             // Lastly, adjust the ranks.
@@ -1901,6 +1912,22 @@
             Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
                     "Calling application must have a foreground activity or a foreground service");
 
+            // If it's a pin shortcut request, and there's already a shortcut with the same ID
+            // that's not visible to the caller (i.e. restore-blocked; meaning it's pinned by
+            // someone already), then we just replace the existing one with this new one,
+            // and then proceed the rest of the process.
+            if (shortcut != null) {
+                final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(
+                        packageName, userId);
+                final String id = shortcut.getId();
+                if (ps.isShortcutExistsAndInvisibleToPublisher(id)) {
+
+                    ps.updateInvisibleShortcutForPinRequestWith(shortcut);
+
+                    packageShortcutsChanged(packageName, userId);
+                }
+            }
+
             // Send request to the launcher, if supported.
             ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, extras,
                     userId, resultIntent);
@@ -1922,15 +1949,21 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
+                    /*ignoreInvisible=*/ true);
 
             final String disabledMessageString =
                     (disabledMessage == null) ? null : disabledMessage.toString();
 
             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
-                ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
+                final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
+                if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
+                    continue;
+                }
+                ps.disableWithId(id,
                         disabledMessageString, disabledMessageResId,
-                        /* overrideImmutable=*/ false);
+                        /* overrideImmutable=*/ false, /*ignoreInvisible=*/ true,
+                        ShortcutInfo.DISABLED_REASON_BY_APP);
             }
 
             // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
@@ -1951,10 +1984,15 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
+                    /*ignoreInvisible=*/ true);
 
             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
-                ps.enableWithId((String) shortcutIds.get(i));
+                final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
+                if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
+                    continue;
+                }
+                ps.enableWithId(id);
             }
         }
         packageShortcutsChanged(packageName, userId);
@@ -1973,11 +2011,15 @@
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
-            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+            ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
+                    /*ignoreInvisible=*/ true);
 
             for (int i = shortcutIds.size() - 1; i >= 0; i--) {
-                ps.deleteDynamicWithId(
-                        Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
+                final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
+                if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
+                    continue;
+                }
+                ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
             }
 
             // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
@@ -1996,7 +2038,7 @@
             throwIfUserLockedL(userId);
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
-            ps.deleteAllDynamicShortcuts();
+            ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
         }
         packageShortcutsChanged(packageName, userId);
 
@@ -2013,7 +2055,7 @@
 
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
-                    ShortcutInfo::isDynamic);
+                    ShortcutInfo::isDynamicVisible);
         }
     }
 
@@ -2027,7 +2069,7 @@
 
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
-                    ShortcutInfo::isManifestShortcut);
+                    ShortcutInfo::isManifestVisible);
         }
     }
 
@@ -2041,7 +2083,7 @@
 
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
-                    ShortcutInfo::isPinned);
+                    ShortcutInfo::isPinnedVisible);
         }
     }
 
@@ -2513,7 +2555,7 @@
                         getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
                 launcher.attemptToRestoreIfNeededAndSave();
 
-                launcher.pinShortcuts(userId, packageName, shortcutIds);
+                launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
             }
             packageShortcutsChanged(packageName, userId);
 
@@ -3343,7 +3385,7 @@
         return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
     }
 
-    boolean shouldBackupApp(PackageInfo pi) {
+    static boolean shouldBackupApp(PackageInfo pi) {
         return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
     }
 
@@ -3371,7 +3413,7 @@
             // Set the version code for the launchers.
             // We shouldn't do this for publisher packages, because we don't want to update the
             // version code without rescanning the manifest.
-            user.forAllLaunchers(launcher -> launcher.ensureVersionInfo());
+            user.forAllLaunchers(launcher -> launcher.ensurePackageInfo());
 
             // Save to the filesystem.
             scheduleSaveUser(userId);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 55e6d28..48eccd0 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -364,9 +364,6 @@
     private void saveShortcutPackageItem(XmlSerializer out,
             ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
         if (forBackup) {
-            if (!mService.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
-                return; // Don't save.
-            }
             if (spi.getPackageUserId() != spi.getOwnerUserId()) {
                 return; // Don't save cross-user information.
             }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a6b05d7..c18a71d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -96,6 +96,7 @@
             UserManager.DISALLOW_SMS,
             UserManager.DISALLOW_FUN,
             UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
             UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
             UserManager.DISALLOW_OUTGOING_BEAM,
             UserManager.DISALLOW_WALLPAPER,
@@ -156,6 +157,7 @@
     private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
             UserManager.DISALLOW_ADJUST_VOLUME,
             UserManager.DISALLOW_BLUETOOTH_SHARING,
+            UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
             UserManager.DISALLOW_RUN_IN_BACKGROUND,
             UserManager.DISALLOW_UNMUTE_MICROPHONE,
             UserManager.DISALLOW_UNMUTE_DEVICE
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 09a6e9c..71d3202 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -48,6 +48,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -77,7 +78,7 @@
 
     final String name;
 
-    @PermissionType final int type;
+    final @PermissionType int type;
 
     String sourcePackageName;
 
@@ -252,12 +253,12 @@
         return changed;
     }
 
-    public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) {
+    public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
         if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
                 + getName() + " pkg=" + getSourcePackageName()
                 + " info=" + pendingPermissionInfo);
         if (sourcePackageSetting == null && pendingPermissionInfo != null) {
-            final BasePermission tree = findPermissionTreeLP(permissionTrees, name);
+            final BasePermission tree = findPermissionTree(permissionTrees, name);
             if (tree != null && tree.perm != null) {
                 sourcePackageSetting = tree.sourcePackageSetting;
                 perm = new PackageParser.Permission(tree.perm.owner,
@@ -269,8 +270,8 @@
         }
     }
 
-    public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
-            @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees,
+    static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
+            @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
             boolean chatty) {
         final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
         // Allow system apps to redefine non-system permissions
@@ -300,7 +301,7 @@
         if (bp.perm == null) {
             if (bp.sourcePackageName == null
                     || bp.sourcePackageName.equals(p.info.packageName)) {
-                final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
+                final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
                 if (tree == null
                         || tree.sourcePackageName.equals(p.info.packageName)) {
                     bp.sourcePackageSetting = pkgSetting;
@@ -345,12 +346,12 @@
         return bp;
     }
 
-    public static BasePermission enforcePermissionTreeLP(
-            Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
+    static BasePermission enforcePermissionTree(
+            Collection<BasePermission> permissionTrees, String permName, int callingUid) {
         if (permName != null) {
-            BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
+            BasePermission bp = findPermissionTree(permissionTrees, permName);
             if (bp != null) {
-                if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
+                if (bp.uid == UserHandle.getAppId(callingUid)) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid " + callingUid
@@ -373,9 +374,9 @@
         }
     }
 
-    private static BasePermission findPermissionTreeLP(
-            Map<String, BasePermission> permissionTrees, String permName) {
-        for (BasePermission bp : permissionTrees.values()) {
+    private static BasePermission findPermissionTree(
+            Collection<BasePermission> permissionTrees, String permName) {
+        for (BasePermission bp : permissionTrees) {
             if (permName.startsWith(bp.name) &&
                     permName.length() > bp.name.length() &&
                     permName.charAt(bp.name.length()) == '.') {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 3b20b42..8aac52a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -31,6 +31,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Internal interfaces to be used by other components within the system server.
@@ -81,11 +82,26 @@
             @NonNull int[] allUserIds);
 
 
-    public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+    /**
+     * Add all permissions in the given package.
+     * <p>
+     * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
+     * the permission settings.
+     */
+    public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
             int callingUid, @Nullable PermissionCallback callback);
-    public abstract void removePermission(@NonNull String permName, int callingUid,
+    public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
             @Nullable PermissionCallback callback);
 
+    public abstract int updatePermissions(@Nullable String changingPkg,
+            @Nullable PackageParser.Package pkgInfo, int flags);
+    public abstract int updatePermissionTrees(@Nullable String changingPkg,
+            @Nullable PackageParser.Package pkgInfo, int flags);
+
+    public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
+
     public abstract int getPermissionFlags(@NonNull String permName,
             @NonNull String packageName, int callingUid, int userId);
     /**
@@ -98,8 +114,6 @@
      */
     public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
             @PermissionInfoFlags int flags, int callingUid);
-    public abstract boolean isPermissionAppOp(@NonNull String permName);
-    public abstract boolean isPermissionInstant(@NonNull String permName);
 
     /**
      * Updates the flags associated with a permission by replacing the flags in
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6c031a6..062aa33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,6 +69,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -260,7 +261,7 @@
 //            }
 
             final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
-            for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+            for (BasePermission bp : mSettings.mPermissions.values()) {
                 final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
                 if (pi != null) {
                     out.add(pi);
@@ -305,7 +306,98 @@
         return protectionLevel;
     }
 
-    private boolean addPermission(
+    private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
+        final int N = pkg.permissions.size();
+        for (int i=0; i<N; i++) {
+            PackageParser.Permission p = pkg.permissions.get(i);
+
+            // Assume by default that we did not install this permission into the system.
+            p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
+
+            // Now that permission groups have a special meaning, we ignore permission
+            // groups for legacy apps to prevent unexpected behavior. In particular,
+            // permissions for one app being granted to someone just because they happen
+            // to be in a group defined by another app (before this had no implications).
+            if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+                p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group);
+                // Warn for a permission in an unknown group.
+                if (PackageManagerService.DEBUG_PERMISSIONS
+                        && p.info.group != null && p.group == null) {
+                    Slog.i(TAG, "Permission " + p.info.name + " from package "
+                            + p.info.packageName + " in an unknown group " + p.info.group);
+                }
+            }
+
+            synchronized (PermissionManagerService.this.mLock) {
+                if (p.tree) {
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
+                            mSettings.getAllPermissionTreesLocked(), chatty);
+                    mSettings.putPermissionTreeLocked(p.info.name, bp);
+                } else {
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            mSettings.getPermissionLocked(p.info.name),
+                            p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
+                    mSettings.putPermissionLocked(p.info.name, bp);
+                }
+            }
+        }
+    }
+
+    private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+        synchronized (mLock) {
+            int N = pkg.permissions.size();
+            StringBuilder r = null;
+            for (int i=0; i<N; i++) {
+                PackageParser.Permission p = pkg.permissions.get(i);
+                BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
+                if (bp == null) {
+                    bp = mSettings.mPermissionTrees.get(p.info.name);
+                }
+                if (bp != null && bp.isPermission(p)) {
+                    bp.setPermission(null);
+                    if (PackageManagerService.DEBUG_REMOVE && chatty) {
+                        if (r == null) {
+                            r = new StringBuilder(256);
+                        } else {
+                            r.append(' ');
+                        }
+                        r.append(p.info.name);
+                    }
+                }
+                if (p.isAppOp()) {
+                    ArraySet<String> appOpPkgs =
+                            mSettings.mAppOpPermissionPackages.get(p.info.name);
+                    if (appOpPkgs != null) {
+                        appOpPkgs.remove(pkg.packageName);
+                    }
+                }
+            }
+            if (r != null) {
+                if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
+            }
+
+            N = pkg.requestedPermissions.size();
+            r = null;
+            for (int i=0; i<N; i++) {
+                String perm = pkg.requestedPermissions.get(i);
+                if (mSettings.isPermissionAppOp(perm)) {
+                    ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
+                    if (appOpPkgs != null) {
+                        appOpPkgs.remove(pkg.packageName);
+                        if (appOpPkgs.isEmpty()) {
+                            mSettings.mAppOpPermissionPackages.remove(perm);
+                        }
+                    }
+                }
+            }
+            if (r != null) {
+                if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
+            }
+        }
+    }
+
+    private boolean addDynamicPermission(
             PermissionInfo info, int callingUid, PermissionCallback callback) {
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             throw new SecurityException("Instant apps can't add permissions");
@@ -313,8 +405,7 @@
         if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
             throw new SecurityException("Label must be specified in permission");
         }
-        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
-                info.name, callingUid);
+        final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
         final boolean added;
         final boolean changed;
         synchronized (mLock) {
@@ -341,13 +432,12 @@
         return added;
     }
 
-    private void removePermission(
+    private void removeDynamicPermission(
             String permName, int callingUid, PermissionCallback callback) {
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             throw new SecurityException("Instant applications don't have access to this method");
         }
-        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
-                permName, callingUid);
+        final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
         synchronized (mLock) {
             final BasePermission bp = mSettings.getPermissionLocked(permName);
             if (bp == null) {
@@ -713,7 +803,21 @@
         return runtimePermissionChangedUserIds;
     }
 
-    private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+    private String[] getAppOpPermissionPackages(String permName) {
+        if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
+            if (pkgs == null) {
+                return null;
+            }
+            return pkgs.toArray(new String[pkgs.size()]);
+        }
+    }
+
+    private int getPermissionFlags(
+            String permName, String packageName, int callingUid, int userId) {
         if (!mUserManagerInt.exists(userId)) {
             return 0;
         }
@@ -741,6 +845,96 @@
         return permissionsState.getPermissionFlags(permName, userId);
     }
 
+    private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) {
+        Set<BasePermission> needsUpdate = null;
+        synchronized (mLock) {
+            final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
+            while (it.hasNext()) {
+                final BasePermission bp = it.next();
+                if (bp.isDynamic()) {
+                    bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
+                }
+                if (bp.getSourcePackageSetting() != null) {
+                    if (packageName != null && packageName.equals(bp.getSourcePackageName())
+                        && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+                        Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+                                + " from package " + bp.getSourcePackageName());
+                        flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+                        it.remove();
+                    }
+                    continue;
+                }
+                if (needsUpdate == null) {
+                    needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
+                }
+                needsUpdate.add(bp);
+            }
+        }
+        if (needsUpdate != null) {
+            for (final BasePermission bp : needsUpdate) {
+                final PackageParser.Package pkg =
+                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                synchronized (mLock) {
+                    if (pkg != null && pkg.mExtras != null) {
+                        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                        if (bp.getSourcePackageSetting() == null) {
+                            bp.setSourcePackageSetting(ps);
+                        }
+                        continue;
+                    }
+                    Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+                            + " from package " + bp.getSourcePackageName());
+                    mSettings.removePermissionLocked(bp.getName());
+                }
+            }
+        }
+        return flags;
+    }
+
+    private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo,
+            int flags) {
+        Set<BasePermission> needsUpdate = null;
+        synchronized (mLock) {
+            final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
+            while (it.hasNext()) {
+                final BasePermission bp = it.next();
+                if (bp.getSourcePackageSetting() != null) {
+                    if (packageName != null && packageName.equals(bp.getSourcePackageName())
+                        && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+                        Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+                                + " from package " + bp.getSourcePackageName());
+                        flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+                        it.remove();
+                    }
+                    continue;
+                }
+                if (needsUpdate == null) {
+                    needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
+                }
+                needsUpdate.add(bp);
+            }
+        }
+        if (needsUpdate != null) {
+            for (final BasePermission bp : needsUpdate) {
+                final PackageParser.Package pkg =
+                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                synchronized (mLock) {
+                    if (pkg != null && pkg.mExtras != null) {
+                        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                        if (bp.getSourcePackageSetting() == null) {
+                            bp.setSourcePackageSetting(ps);
+                        }
+                        continue;
+                    }
+                    Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+                            + " from package " + bp.getSourcePackageName());
+                    mSettings.removePermissionLocked(bp.getName());
+                }
+            }
+        }
+        return flags;
+    }
+
     private void updatePermissionFlags(String permName, String packageName, int flagMask,
             int flagValues, int callingUid, int userId, PermissionCallback callback) {
         if (!mUserManagerInt.exists(userId)) {
@@ -872,7 +1066,7 @@
 
     private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
         int size = 0;
-        for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+        for (BasePermission perm : mSettings.mPermissions.values()) {
             size += tree.calculateFootprint(perm);
         }
         return size;
@@ -889,6 +1083,15 @@
         }
     }
 
+    private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
+        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+            if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Get the first event id for the permission.
      *
@@ -951,14 +1154,22 @@
 
     private class PermissionManagerInternalImpl extends PermissionManagerInternal {
         @Override
-        public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
-                PermissionCallback callback) {
-            return PermissionManagerService.this.addPermission(info, callingUid, callback);
+        public void addAllPermissions(Package pkg, boolean chatty) {
+            PermissionManagerService.this.addAllPermissions(pkg, chatty);
         }
         @Override
-        public void removePermission(String permName, int callingUid,
+        public void removeAllPermissions(Package pkg, boolean chatty) {
+            PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+        }
+        @Override
+        public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
                 PermissionCallback callback) {
-            PermissionManagerService.this.removePermission(permName, callingUid, callback);
+            return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
+        }
+        @Override
+        public void removeDynamicPermission(String permName, int callingUid,
+                PermissionCallback callback) {
+            PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
         }
         @Override
         public void grantRuntimePermission(String permName, String packageName,
@@ -993,12 +1204,26 @@
                     (SharedUserSetting) suSetting, allUserIds);
         }
         @Override
+        public String[] getAppOpPermissionPackages(String permName) {
+            return PermissionManagerService.this.getAppOpPermissionPackages(permName);
+        }
+        @Override
         public int getPermissionFlags(String permName, String packageName, int callingUid,
                 int userId) {
             return PermissionManagerService.this.getPermissionFlags(permName, packageName,
                     callingUid, userId);
         }
         @Override
+        public int updatePermissions(String packageName,
+                PackageParser.Package pkgInfo, int flags) {
+            return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags);
+        }
+        @Override
+        public int updatePermissionTrees(String packageName,
+                PackageParser.Package pkgInfo, int flags) {
+            return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags);
+        }
+        @Override
         public void updatePermissionFlags(String permName, String packageName, int flagMask,
                 int flagValues, int callingUid, int userId, PermissionCallback callback) {
             PermissionManagerService.this.updatePermissionFlags(
@@ -1038,20 +1263,6 @@
             return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
         }
         @Override
-        public boolean isPermissionInstant(String permName) {
-            synchronized (PermissionManagerService.this.mLock) {
-                final BasePermission bp = mSettings.getPermissionLocked(permName);
-                return (bp != null && bp.isInstant());
-            }
-        }
-        @Override
-        public boolean isPermissionAppOp(String permName) {
-            synchronized (PermissionManagerService.this.mLock) {
-                final BasePermission bp = mSettings.getPermissionLocked(permName);
-                return (bp != null && bp.isAppOp());
-            }
-        }
-        @Override
         public PermissionSettings getPermissionSettings() {
             return mSettings;
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 7a2e5ecc..7d125c9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.DumpState;
 import com.android.server.pm.PackageManagerService;
@@ -35,6 +36,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * Permissions and other related data. This class is not meant for
@@ -49,8 +51,25 @@
      * All of the permissions known to the system. The mapping is from permission
      * name to permission object.
      */
-    private final ArrayMap<String, BasePermission> mPermissions =
+    @GuardedBy("mLock")
+    final ArrayMap<String, BasePermission> mPermissions =
             new ArrayMap<String, BasePermission>();
+
+    /**
+     * All permission trees known to the system. The mapping is from permission tree
+     * name to permission object.
+     */
+    @GuardedBy("mLock")
+    final ArrayMap<String, BasePermission> mPermissionTrees =
+            new ArrayMap<String, BasePermission>();
+
+    /**
+     * Set of packages that request a particular app op. The mapping is from permission
+     * name to package names.
+     */
+    @GuardedBy("mLock")
+    final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
     private final Object mLock;
 
     PermissionSettings(@NonNull Context context, @NonNull Object lock) {
@@ -65,15 +84,23 @@
         }
     }
 
+    public void addAppOpPackage(String permName, String packageName) {
+        ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName);
+        if (pkgs == null) {
+            pkgs = new ArraySet<>();
+            mAppOpPermissionPackages.put(permName, pkgs);
+        }
+        pkgs.add(packageName);
+    }
+
     /**
      * Transfers ownership of permissions from one package to another.
      */
-    public void transferPermissions(String origPackageName, String newPackageName,
-            ArrayMap<String, BasePermission> permissionTrees) {
+    public void transferPermissions(String origPackageName, String newPackageName) {
         synchronized (mLock) {
             for (int i=0; i<2; i++) {
                 ArrayMap<String, BasePermission> permissions =
-                        i == 0 ? permissionTrees : mPermissions;
+                        i == 0 ? mPermissionTrees : mPermissions;
                 for (BasePermission bp : permissions.values()) {
                     bp.transfer(origPackageName, newPackageName);
                 }
@@ -94,9 +121,26 @@
         }
     }
 
+    public void readPermissionTrees(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            readPermissions(mPermissionTrees, parser);
+        }
+    }
+
     public void writePermissions(XmlSerializer serializer) throws IOException {
-        for (BasePermission bp : mPermissions.values()) {
-            bp.writeLPr(serializer);
+        synchronized (mLock) {
+            for (BasePermission bp : mPermissions.values()) {
+                bp.writeLPr(serializer);
+            }
+        }
+    }
+
+    public void writePermissionTrees(XmlSerializer serializer) throws IOException {
+        synchronized (mLock) {
+            for (BasePermission bp : mPermissionTrees.values()) {
+                bp.writeLPr(serializer);
+            }
         }
     }
 
@@ -128,6 +172,22 @@
                 printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
                         externalStorageEnforced, printedSomething, dumpState);
             }
+            if (packageName == null && permissionNames == null) {
+                for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+                    if (iperm == 0) {
+                        if (dumpState.onTitlePrinted())
+                            pw.println();
+                        pw.println("AppOp Permissions:");
+                    }
+                    pw.print("  AppOp Permission ");
+                    pw.print(mAppOpPermissionPackages.keyAt(iperm));
+                    pw.println(":");
+                    ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+                    for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+                        pw.print("    "); pw.println(pkgs.valueAt(ipkg));
+                    }
+                }
+            }
         }
     }
 
@@ -135,15 +195,58 @@
         return mPermissions.get(permName);
     }
 
+    @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
+        return mPermissionTrees.get(permName);
+    }
+
     void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
         mPermissions.put(permName, permission);
     }
 
+    void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
+        mPermissionTrees.put(permName, permission);
+    }
+
     void removePermissionLocked(@NonNull String permName) {
         mPermissions.remove(permName);
     }
 
-    Collection<BasePermission> getAllPermissionsLocked() {
+    void removePermissionTreeLocked(@NonNull String permName) {
+        mPermissionTrees.remove(permName);
+    }
+
+    @NonNull Collection<BasePermission> getAllPermissionsLocked() {
         return mPermissions.values();
     }
+
+    @NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
+        return mPermissionTrees.values();
+    }
+
+    /**
+     * Returns the permission tree for the given permission.
+     * @throws SecurityException If the calling UID is not allowed to add permissions to the
+     * found permission tree.
+     */
+    @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) {
+        synchronized (mLock) {
+            return BasePermission.enforcePermissionTree(
+                    mPermissionTrees.values(), permName, callingUid);
+        }
+    }
+
+    public boolean isPermissionInstant(String permName) {
+        synchronized (mLock) {
+            final BasePermission bp = mPermissions.get(permName);
+            return (bp != null && bp.isInstant());
+        }
+    }
+
+    boolean isPermissionAppOp(String permName) {
+        synchronized (mLock) {
+            final BasePermission bp = mPermissions.get(permName);
+            return (bp != null && bp.isAppOp());
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 342ec4b..7a2e630 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -58,6 +58,9 @@
 
     public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
         if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
+        if (mStatusBarInternal.isGlobalActionsDisabled()) {
+            return;
+        }
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = deviceProvisioned;
         mShowing = true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index db7817e..ceb0ad0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,13 +18,14 @@
 
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
@@ -552,7 +553,6 @@
 
     int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
     int mUserRotation = Surface.ROTATION_0;
-    boolean mAccelerometerDefault;
 
     boolean mSupportAutoRotation;
     int mAllowAllRotations = -1;
@@ -707,7 +707,6 @@
     Intent mVrHeadsetHomeIntent;
     boolean mSearchKeyShortcutPending;
     boolean mConsumeSearchKeyUp;
-    boolean mAssistKeyLongPressed;
     boolean mPendingMetaAction;
     boolean mPendingCapsLockToggle;
     int mMetaState;
@@ -838,6 +837,8 @@
     private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 24;
     private static final int MSG_SYSTEM_KEY_PRESS = 25;
     private static final int MSG_HANDLE_ALL_APPS = 26;
+    private static final int MSG_LAUNCH_ASSIST = 27;
+    private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 28;
 
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -879,8 +880,16 @@
                 case MSG_HIDE_BOOT_MESSAGE:
                     handleHideBootMessage();
                     break;
+                case MSG_LAUNCH_ASSIST:
+                    final int deviceId = msg.arg1;
+                    final String hint = (String) msg.obj;
+                    launchAssistAction(hint, deviceId);
+                    break;
+                case MSG_LAUNCH_ASSIST_LONG_PRESS:
+                    launchAssistLongPressAction();
+                    break;
                 case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
-                    launchVoiceAssistWithWakeLock(msg.arg1 != 0);
+                    launchVoiceAssistWithWakeLock();
                     break;
                 case MSG_POWER_DELAYED_PRESS:
                     powerPress((Long)msg.obj, msg.arg1 != 0, msg.arg2);
@@ -910,7 +919,7 @@
                     disposeInputConsumer((InputConsumer) msg.obj);
                     break;
                 case MSG_BACK_DELAYED_PRESS:
-                    backMultiPressAction((Long) msg.obj, msg.arg1);
+                    backMultiPressAction(msg.arg1);
                     finishBackKeyPress();
                     break;
                 case MSG_ACCESSIBILITY_SHORTCUT:
@@ -1414,7 +1423,7 @@
         }
     }
 
-    private void backMultiPressAction(long eventTime, int count) {
+    private void backMultiPressAction(int count) {
         if (count >= PANIC_PRESS_BACK_COUNT) {
             switch (mPanicPressOnBackBehavior) {
                 case PANIC_PRESS_BACK_NOTHING:
@@ -1583,7 +1592,7 @@
         }
     }
 
-    private void sleepPress(long eventTime) {
+    private void sleepPress() {
         if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
             launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
         }
@@ -2270,7 +2279,11 @@
 
         // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
         // http://developer.android.com/guide/practices/screens_support.html#range
-        mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 &&
+        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
+        // so if the orientation is forced, we need to respect that no matter what.
+        boolean isCar = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar) &&
                 res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
                 // For debug purposes the next line turns this feature off with:
                 // $ adb shell setprop config.override_forced_orient true
@@ -2844,7 +2857,7 @@
 
         boolean keyguardLocked = isKeyguardLocked();
         boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
-                && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+                && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
                 || hideDockDivider;
     }
@@ -3537,44 +3550,11 @@
                 toggleKeyboardShortcutsMenu(event.getDeviceId());
             }
         } else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
-            if (down) {
-                if (repeatCount == 0) {
-                    mAssistKeyLongPressed = false;
-                } else if (repeatCount == 1) {
-                    mAssistKeyLongPressed = true;
-                    if (!keyguardOn) {
-                         launchAssistLongPressAction();
-                    }
-                }
-            } else {
-                if (mAssistKeyLongPressed) {
-                    mAssistKeyLongPressed = false;
-                } else {
-                    if (!keyguardOn) {
-                        launchAssistAction(null, event.getDeviceId());
-                    }
-                }
-            }
+            Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
             return -1;
         } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) {
-            if (!down) {
-                Intent voiceIntent;
-                if (!keyguardOn) {
-                    voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
-                } else {
-                    IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
-                            ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-                    if (dic != null) {
-                        try {
-                            dic.exitIdle("voice-search");
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
-                    voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
-                }
-                startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
-            }
+            Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing");
+            return -1;
         } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
             if (down && repeatCount == 0) {
                 mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -5445,7 +5425,10 @@
 
         boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
                 && attrs.type < FIRST_SYSTEM_WINDOW;
-        final int stackId = win.getStackId();
+        final int windowingMode = win.getWindowingMode();
+        final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
+                windowingMode == WINDOWING_MODE_FULLSCREEN
+                        || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
         if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
                 mForceStatusBar = true;
@@ -5464,7 +5447,7 @@
             // represent should be hidden or if we should hide the lockscreen. For attached app
             // windows we defer the decision to the window it is attached to.
             if (appWindow && attached == null) {
-                if (attrs.isFullscreen() && StackId.normallyFullscreenWindows(stackId)) {
+                if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
                     mTopFullscreenOpaqueWindowState = win;
                     if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -5495,7 +5478,7 @@
 
         // Keep track of the window if it's dimming but not necessarily fullscreen.
         if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
-                && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
+                && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
             mTopFullscreenOpaqueOrDimmingWindowState = win;
         }
 
@@ -5503,7 +5486,7 @@
         // separately, because both the "real fullscreen" opaque window and the one for the docked
         // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
         if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
-                && attrs.isFullscreen() && stackId == DOCKED_STACK_ID) {
+                && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             mTopDockedOpaqueWindowState = win;
             if (mTopDockedOpaqueOrDimmingWindowState == null) {
                 mTopDockedOpaqueOrDimmingWindowState = win;
@@ -5513,7 +5496,7 @@
         // Also keep track of any windows that are dimming but not necessarily fullscreen in the
         // docked stack.
         if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
-                && stackId == DOCKED_STACK_ID) {
+                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             mTopDockedOpaqueOrDimmingWindowState = win;
         }
 
@@ -5608,8 +5591,9 @@
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
                     }
                 } else if (topIsFullscreen
-                        && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
-                        && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
+                        && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM)
+                        && !mWindowManagerInternal.isStackVisible(
+                                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
                     if (mStatusBarController.setBarShowingLw(false)) {
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -6191,7 +6175,7 @@
                     useHapticFeedback = false; // suppress feedback if already non-interactive
                 }
                 if (down) {
-                    sleepPress(event.getEventTime());
+                    sleepPress();
                 } else {
                     sleepRelease(event.getEventTime());
                 }
@@ -6262,18 +6246,30 @@
                 }
                 break;
             }
-            case KeyEvent.KEYCODE_VOICE_ASSIST: {
-                // Only do this if we would otherwise not pass it to the user. In that case,
-                // interceptKeyBeforeDispatching would apply a similar but different policy in
-                // order to invoke voice assist actions. Note that we need to make a copy of the
-                // key event here because the original key event will be recycled when we return.
-                if ((result & ACTION_PASS_TO_USER) == 0 && !down) {
-                    mBroadcastWakeLock.acquire();
-                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK,
-                            keyguardActive ? 1 : 0, 0);
+            case KeyEvent.KEYCODE_ASSIST: {
+                final boolean longPressed = event.getRepeatCount() > 0;
+                if (down && longPressed) {
+                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST_LONG_PRESS);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
                 }
+                if (!down && !longPressed) {
+                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
+                            0 /* unused */, null /* hint */);
+                    msg.setAsynchronous(true);
+                    msg.sendToTarget();
+                }
+                result &= ~ACTION_PASS_TO_USER;
+                break;
+            }
+            case KeyEvent.KEYCODE_VOICE_ASSIST: {
+                if (!down) {
+                    mBroadcastWakeLock.acquire();
+                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
+                    msg.setAsynchronous(true);
+                    msg.sendToTarget();
+                }
+                result &= ~ACTION_PASS_TO_USER;
                 break;
             }
             case KeyEvent.KEYCODE_WINDOW: {
@@ -6542,18 +6538,22 @@
         }
     }
 
-    void launchVoiceAssistWithWakeLock(boolean keyguardActive) {
-        IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-        if (dic != null) {
-            try {
-                dic.exitIdle("voice-search");
-            } catch (RemoteException e) {
+    void launchVoiceAssistWithWakeLock() {
+        final Intent voiceIntent;
+        if (!keyguardOn()) {
+            voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+        } else {
+            IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
+                    ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+            if (dic != null) {
+                try {
+                    dic.exitIdle("voice-search");
+                } catch (RemoteException e) {
+                }
             }
+            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
         }
-        Intent voiceIntent =
-            new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, keyguardActive);
         startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
         mBroadcastWakeLock.release();
     }
@@ -8009,9 +8009,10 @@
     }
 
     private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
-        final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+        final boolean dockedStackVisible =
+                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final boolean freeformStackVisible =
-                mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
+                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
         final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
 
         // We need to force system bars when the docked stack is visible, when the freeform stack
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f1fb3e7..ca3dd05 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -20,15 +20,25 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.IntentFilter;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Slog;
 
+import java.util.ArrayList;
+import java.util.List;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
@@ -53,6 +63,8 @@
 
     private final PendingIntent mAnomalyAlarmIntent;
     private final PendingIntent mPollingAlarmIntent;
+    private final BroadcastReceiver mAppUpdateReceiver;
+    private final BroadcastReceiver mUserUpdateReceiver;
 
     public StatsCompanionService(Context context) {
         super();
@@ -63,8 +75,113 @@
                 new Intent(mContext, AnomalyAlarmReceiver.class), 0);
         mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(mContext, PollingAlarmReceiver.class), 0);
+        mAppUpdateReceiver = new AppUpdateReceiver();
+        mUserUpdateReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                synchronized (sStatsdLock) {
+                    sStatsd = fetchStatsdService();
+                    if (sStatsd == null) {
+                        Slog.w(TAG, "Could not access statsd");
+                        return;
+                    }
+                    try {
+                        // Pull the latest state of UID->app name, version mapping.
+                        // Needed since the new user basically has a version of every app.
+                        informAllUidsLocked(context);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                        forgetEverything();
+                    }
+                }
+            }
+        };
+        Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
     }
 
+    private final static int[] toIntArray(List<Integer> list){
+        int[] ret = new int[list.size()];
+        for(int i = 0;i < ret.length;i++) {
+            ret[i] = list.get(i);
+        }
+        return ret;
+    }
+
+    // Assumes that sStatsdLock is held.
+    private final void informAllUidsLocked(Context context) throws RemoteException {
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        PackageManager pm = context.getPackageManager();
+        final List<UserInfo> users = um.getUsers(true);
+        if (DEBUG) {
+            Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
+        }
+
+        List<Integer> uids = new ArrayList();
+        List<Integer> versions = new ArrayList();
+        List<String> apps = new ArrayList();
+
+        // Add in all the apps for every user/profile.
+        for (UserInfo profile : users) {
+          List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+          for (int j = 0; j < pi.size(); j++) {
+              if (pi.get(j).applicationInfo != null) {
+                  uids.add(pi.get(j).applicationInfo.uid);
+                  versions.add(pi.get(j).versionCode);
+                  apps.add(pi.get(j).packageName);
+              }
+          }
+        }
+        sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
+            String[apps.size()]));
+        if (DEBUG) {
+            Slog.w(TAG, "Sent data for "+uids.size() +" apps");
+        }
+    }
+
+    public final static class AppUpdateReceiver extends BroadcastReceiver  {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
+            /**
+             * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
+             * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
+             */
+            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
+                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                return; // Keep only replacing or normal add and remove.
+            }
+            synchronized (sStatsdLock) {
+                if (sStatsd == null) {
+                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                    return;
+                }
+                try {
+                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
+                        Bundle b = intent.getExtras();
+                        int uid = b.getInt(Intent.EXTRA_UID);
+                        boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                        if (!replacing) {
+                            // Don't bother sending an update if we're right about to get another
+                            // intent for the new version that's added.
+                            PackageManager pm = context.getPackageManager();
+                            String app = intent.getData().getSchemeSpecificPart();
+                            sStatsd.informOnePackageRemoved(app, uid);
+                        }
+                    } else {
+                        PackageManager pm = context.getPackageManager();
+                        Bundle b = intent.getExtras();
+                        int uid = b.getInt(Intent.EXTRA_UID);
+                        String app = intent.getData().getSchemeSpecificPart();
+                        PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
+                        sStatsd.informOnePackage(app, uid, pi.versionCode);
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
+                }
+            }
+        }
+    };
+
     public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -275,6 +392,23 @@
                     Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
                     forgetEverything();
                 }
+                // Setup broadcast receiver for updates
+                IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
+                filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+                filter.addDataScheme("package");
+                mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
+                    null);
+
+                // Setup receiver for user initialize (which happens once for a new user) and
+                // if a user is removed.
+                filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
+                filter.addAction(Intent.ACTION_USER_REMOVED);
+                mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
+                    filter, null, null);
+
+                // Pull the latest state of UID->app name, version mapping when statsd starts.
+                informAllUidsLocked(mContext);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
                 forgetEverything();
@@ -293,6 +427,8 @@
     private void forgetEverything() {
         synchronized (sStatsdLock) {
             sStatsd = null;
+            mContext.unregisterReceiver(mAppUpdateReceiver);
+            mContext.unregisterReceiver(mUserUpdateReceiver);
             cancelAnomalyAlarm();
             cancelPollingAlarms();
         }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 0884678..b07fe98 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -77,6 +77,7 @@
 
     void setCurrentUser(int newUserId);
 
+    boolean isGlobalActionsDisabled();
     void setGlobalActionsListener(GlobalActionsListener listener);
     void showGlobalActions();
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index bdfbe48..c78a340 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.statusbar;
 
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+
 import android.app.ActivityThread;
 import android.app.StatusBarManager;
 import android.content.ComponentName;
@@ -363,6 +365,11 @@
         }
 
         @Override
+        public boolean isGlobalActionsDisabled() {
+            return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+        }
+
+        @Override
         public void setGlobalActionsListener(GlobalActionsListener listener) {
             mGlobalActionListener = listener;
             mGlobalActionListener.onStatusBarConnectedChanged(mBar != null);
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 6117da7..c1607e9 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1022,20 +1022,6 @@
             }
         }
 
-        @Override
-        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
-            synchronized (mImplLock) {
-                if (mReleased) {
-                    throw new IllegalStateException("Device already released.");
-                }
-            }
-            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
-                return false;
-            }
-            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
-            return false;
-        }
-
         private boolean startCapture(Surface surface, TvStreamConfig config) {
             synchronized (mImplLock) {
                 if (mReleased) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a1eeff8..1b463c7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -823,7 +823,7 @@
 
         // For freeform windows, we can't freeze the bounds at the moment because this would make
         // the resizing unresponsive.
-        if (task == null || task.inFreeformWorkspace()) {
+        if (task == null || task.inFreeformWindowingMode()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 5c29a0a..d206554 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -18,7 +18,6 @@
 
 import static android.graphics.PixelFormat.OPAQUE;
 import static android.view.SurfaceControl.FX_SURFACE_DIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -51,14 +50,8 @@
             int w = r-l;
             int h = b-t;
 
-            if (DEBUG_SURFACE_TRACE) {
-                surface = new WindowSurfaceController.SurfaceTrace(session, "BlackSurface("
-                        + l + ", " + t + ")",
-                        w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-            } else {
-                surface = new SurfaceControl(session, "BlackSurface",
-                        w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-            }
+            surface = new SurfaceControl(session, "BlackSurface",
+                    w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
 
             surface.setAlpha(1);
             surface.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index ae41541..85f468b 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -67,14 +66,9 @@
 
         SurfaceControl ctrl = null;
         try {
-            if (DEBUG_SURFACE_TRACE) {
-                ctrl = new WindowSurfaceController.SurfaceTrace(session, "CircularDisplayMask",
-                        mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
-                        SurfaceControl.HIDDEN);
-            } else {
-                ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x,
-                        mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-            }
+            ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x,
+                    mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+
             ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 9e028d3..5bfea98 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -21,7 +21,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -182,6 +184,11 @@
         return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     }
 
+    public boolean inSplitScreenPrimaryWindowingMode() {
+        return mFullConfiguration.windowConfiguration.getWindowingMode()
+                == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+    }
+
     /**
      * Returns true if this container can be put in either
      * {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
@@ -192,6 +199,14 @@
         return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
     }
 
+    public boolean inPinnedWindowingMode() {
+        return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
+    }
+
+    public boolean inFreeformWindowingMode() {
+        return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+    }
+
     /** Returns the activity type associated with the the configuration container. */
     /*@WindowConfiguration.ActivityType*/
     public int getActivityType() {
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 708973d..48181d3 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -105,16 +104,10 @@
     private void constructSurface(WindowManagerService service) {
         service.openSurfaceTransaction();
         try {
-            if (DEBUG_SURFACE_TRACE) {
-                mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
-                    "DimSurface",
+            mDimSurface = new SurfaceControl(service.mFxSession, mName,
                     16, 16, PixelFormat.OPAQUE,
                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-            } else {
-                mDimSurface = new SurfaceControl(service.mFxSession, mName,
-                    16, 16, PixelFormat.OPAQUE,
-                    SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-            }
+
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
                     "  DIM " + mDimSurface + ": CREATE");
             mDimSurface.setLayerStack(mDisplayId);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0e68a8f..05afd55 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,10 +16,8 @@
 
 package com.android.server.wm;
 
-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.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -119,6 +117,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
+import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -979,8 +978,8 @@
             }
 
             // In the presence of the PINNED stack or System Alert
-            // windows we unforuntately can not seamlessly rotate.
-            if (getStackById(PINNED_STACK_ID) != null) {
+            // windows we unfortunately can not seamlessly rotate.
+            if (getStack(WINDOWING_MODE_PINNED) != null) {
                 mayRotateSeamlessly = false;
             }
             for (int i = 0; i < mService.mSessions.size(); i++) {
@@ -1773,7 +1772,7 @@
             mTaskStackContainers.addStackToDisplay(stack, onTop);
         }
 
-        if (stackId == DOCKED_STACK_ID) {
+        if (stack.inSplitScreenPrimaryWindowingMode()) {
             mDividerControllerLocked.notifyDockedStackExistsChanged(true);
         }
         return stack;
@@ -2037,7 +2036,7 @@
         final WindowState imeWin = mService.mInputMethodWindow;
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
                 && !mDividerControllerLocked.isImeHideRequested();
-        final boolean dockVisible = isStackVisible(DOCKED_STACK_ID);
+        final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
@@ -2260,10 +2259,10 @@
         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());
+    /** Returns true if the stack in the windowing mode is visible. */
+    boolean isStackVisible(int windowingMode) {
+        final TaskStack stack = getStack(windowingMode);
+        return stack != null && stack.isVisible();
     }
 
     /**
@@ -3556,7 +3555,8 @@
 
         @Override
         int getOrientation() {
-            if (isStackVisible(DOCKED_STACK_ID) || isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
+            if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                    || isStackVisible(WINDOWING_MODE_FREEFORM)) {
                 // 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.
@@ -3571,6 +3571,16 @@
             }
 
             final int orientation = super.getOrientation();
+            boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_AUTOMOTIVE);
+            if (isCar) {
+                // In a car, you cannot physically rotate the screen, so it doesn't make sense to
+                // allow anything but the default orientation.
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                        "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
+                return SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+
             if (orientation != SCREEN_ORIENTATION_UNSET
                     && orientation != SCREEN_ORIENTATION_BEHIND) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6f441b9..629af66 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -588,7 +586,7 @@
     private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
         for (int i = apps.size() - 1; i >= 0; i--) {
             final AppWindowToken token = apps.valueAt(i);
-            if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
+            if (token.getTask() != null && token.inSplitScreenPrimaryWindowingMode()) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 3186d3d..19bd8e9 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -57,14 +56,8 @@
 
         SurfaceControl ctrl = null;
         try {
-            if (DEBUG_SURFACE_TRACE) {
-                ctrl = new WindowSurfaceController.SurfaceTrace(session, "EmulatorDisplayOverlay",
-                        mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
-                        SurfaceControl.HIDDEN);
-            } else {
-                ctrl = new SurfaceControl(session, "EmulatorDisplayOverlay", mScreenSize.x,
-                        mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-            }
+            ctrl = new SurfaceControl(session, "EmulatorDisplayOverlay", mScreenSize.x,
+                    mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
             ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 5057f63..238cb9f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
@@ -650,7 +649,7 @@
             final boolean hasFocus = w == mInputFocus;
             final boolean isVisible = w.isVisibleLw();
 
-            if (w.getStackId() == PINNED_STACK_ID) {
+            if (w.inPinnedWindowingMode()) {
                 if (mAddPipInputConsumerHandle
                         && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
                     // Update the bounds of the Pip input consumer to match the Pinned stack
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d5b6d24..8e99be8 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
@@ -24,7 +23,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
-import static com.android.server.wm.WindowSurfaceController.SurfaceTrace;
 import static com.android.server.wm.proto.ScreenRotationAnimationProto.ANIMATION_RUNNING;
 import static com.android.server.wm.proto.ScreenRotationAnimationProto.STARTED;
 
@@ -276,17 +274,10 @@
                     flags |= SurfaceControl.SECURE;
                 }
 
-                if (DEBUG_SURFACE_TRACE) {
-                    mSurfaceControl = new SurfaceTrace(session, "ScreenshotSurface",
-                            mWidth, mHeight,
-                            PixelFormat.OPAQUE, flags);
-                    Slog.w(TAG, "ScreenRotationAnimation ctor: displayOffset="
-                            + mOriginalDisplayRect.toShortString());
-                } else {
-                    mSurfaceControl = new SurfaceControl(session, "ScreenshotSurface",
-                            mWidth, mHeight,
-                            PixelFormat.OPAQUE, flags);
-                }
+                mSurfaceControl = new SurfaceControl(session, "ScreenshotSurface",
+                        mWidth, mHeight,
+                        PixelFormat.OPAQUE, flags);
+
                 // capture a screenshot into the surface we just created
                 Surface sur = new Surface();
                 sur.copyFrom(mSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index c0a4cb7..c0e5fd4 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -280,8 +278,9 @@
             if (stack.getWindowConfiguration().tasksAreFloating()) {
                 // Floating tasks should not be resized to the screen's bounds.
 
-                if (mStackId == PINNED_STACK_ID && bounds.width() == mTmpDisplayBounds.width() &&
-                        bounds.height() == mTmpDisplayBounds.height()) {
+                if (stack.inPinnedWindowingMode()
+                        && bounds.width() == mTmpDisplayBounds.width()
+                        && bounds.height() == mTmpDisplayBounds.height()) {
                     // If the bounds we are animating is the same as the fullscreen stack
                     // dimensions, then apply the same inset calculations that we normally do for
                     // the fullscreen stack, without intersecting it with the display bounds
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7e8d130..6c6934f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 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;
@@ -213,7 +211,7 @@
         // then we want to preserve our insets so that there will not
         // be a jump in the area covered by system decorations. We rely
         // on the pinned animation to later unset this value.
-        if (stack.mStackId == PINNED_STACK_ID) {
+        if (stack.inPinnedWindowingMode()) {
             mPreserveNonFloatingState = true;
         } else {
             mPreserveNonFloatingState = false;
@@ -492,7 +490,7 @@
         final boolean dockedResizing = displayContent != null
                 && displayContent.mDividerControllerLocked.isResizing();
         if (useCurrentBounds()) {
-            if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
+            if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
                 return;
             }
 
@@ -598,14 +596,6 @@
         return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
     }
 
-    boolean inFreeformWorkspace() {
-        return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
-    }
-
-    boolean inPinnedWorkspace() {
-        return mStack != null && mStack.mStackId == PINNED_STACK_ID;
-    }
-
     /**
      * When we are in a floating stack (Freeform, Pinned, ...) we calculate
      * insets differently. However if we are animating to the fullscreen stack
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index c58212c..87de151 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -20,7 +20,6 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.RESIZE_MODE_USER;
 import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -649,7 +648,7 @@
      * shouldn't be shown.
      */
     private int getDimSide(int x) {
-        if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+        if (!mTask.mStack.inFreeformWindowingMode()
                 || !mTask.mStack.fillsParent()
                 || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
             return CTRL_NONE;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index bff24f6..54ef065 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -294,7 +294,9 @@
         decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
         node.end(c);
         final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
-
+        if (hwBitmap == null) {
+            return null;
+        }
         return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
                 topChild.getConfiguration().orientation, mainWindow.mStableInsets,
                 ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6527883..3780d19 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,8 +18,6 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -409,7 +407,7 @@
             return false;
         }
 
-        if (mStackId == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             getAnimationOrCurrentBounds(mTmpRect2);
             boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
                     mTmpRect2, mTmpRect3);
@@ -443,21 +441,19 @@
 
         mTmpRect2.set(mBounds);
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
-        switch (mStackId) {
-            case DOCKED_STACK_ID:
-                repositionDockedStackAfterRotation(mTmpRect2);
-                snapDockedStackAfterRotation(mTmpRect2);
-                final int newDockSide = getDockSide(mTmpRect2);
+        if (inSplitScreenPrimaryWindowingMode()) {
+            repositionDockedStackAfterRotation(mTmpRect2);
+            snapDockedStackAfterRotation(mTmpRect2);
+            final int newDockSide = getDockSide(mTmpRect2);
 
-                // Update the dock create mode and clear the dock create bounds, these
-                // might change after a rotation and the original values will be invalid.
-                mService.setDockedStackCreateStateLocked(
-                        (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
-                                ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
-                                : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
-                        null);
-                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
-                break;
+            // Update the dock create mode and clear the dock create bounds, these
+            // might change after a rotation and the original values will be invalid.
+            mService.setDockedStackCreateStateLocked(
+                    (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+                            ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+                            : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+                    null);
+            mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
         }
 
         mBoundsAfterRotation.set(mTmpRect2);
@@ -688,7 +684,7 @@
 
         Rect bounds = null;
         final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
-        if (mStackId == DOCKED_STACK_ID
+        if (inSplitScreenPrimaryWindowingMode()
                 || (dockedStack != null && inSplitScreenSecondaryWindowingMode()
                         && !dockedStack.fillsParent())) {
             // The existence of a docked stack affects the size of other static stack created since
@@ -703,10 +699,10 @@
             }
             final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
                     == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
+            getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
                     mDisplayContent.mDividerControllerLocked.getContentWidth(),
                     dockedOnTopOrLeft);
-        } else if (mStackId == PINNED_STACK_ID) {
+        } else if (inPinnedWindowingMode()) {
             // Update the bounds based on any changes to the display info
             getAnimationOrCurrentBounds(mTmpRect2);
             boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
@@ -791,7 +787,7 @@
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
         dockedStack.getRawBounds(mTmpRect2);
         final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
-        getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2,
+        getStackDockedModeBounds(mTmpRect, outStackBounds, mTmpRect2,
                 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
 
     }
@@ -800,16 +796,15 @@
      * Outputs the bounds a stack should be given the presence of a docked stack on the display.
      * @param displayRect The bounds of the display the docked stack is on.
      * @param outBounds Output bounds that should be used for the stack.
-     * @param stackId Id of stack we are calculating the bounds for.
      * @param dockedBounds Bounds of the docked stack.
      * @param dockDividerWidth We need to know the width of the divider make to the output bounds
      *                         close to the side of the dock.
      * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
      */
     private void getStackDockedModeBounds(
-            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
+            Rect displayRect, Rect outBounds, Rect dockedBounds, int dockDividerWidth,
             boolean dockOnTopOrLeft) {
-        final boolean dockedStack = stackId == DOCKED_STACK_ID;
+        final boolean dockedStack = inSplitScreenPrimaryWindowingMode();
         final boolean splitHorizontally = displayRect.width() > displayRect.height();
 
         outBounds.set(displayRect);
@@ -866,7 +861,7 @@
     }
 
     void resetDockedStackToMiddle() {
-        if (mStackId != DOCKED_STACK_ID) {
+        if (inSplitScreenPrimaryWindowingMode()) {
             throw new IllegalStateException("Not a docked stack=" + this);
         }
 
@@ -913,7 +908,7 @@
             mAnimationBackgroundSurface = null;
         }
 
-        if (mStackId == DOCKED_STACK_ID) {
+        if (inSplitScreenPrimaryWindowingMode()) {
             mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
         }
 
@@ -1035,8 +1030,8 @@
     }
 
     boolean shouldIgnoreInput() {
-        return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID &&
-                isMinimizedDockAndHomeStackResizable();
+        return isAdjustedForMinimizedDockedStack() ||
+                (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
     }
 
     /**
@@ -1471,7 +1466,7 @@
                 postExclude.set(mTmpRect);
             }
 
-            final boolean isFreeformed = task.inFreeformWorkspace();
+            final boolean isFreeformed = task.inFreeformWindowingMode();
             if (task != focusedTask || isFreeformed) {
                 if (isFreeformed) {
                     // If the task is freeformed, enlarge the area to account for outside
@@ -1529,7 +1524,7 @@
             }
         }
 
-        if (mStackId == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             try {
                 mService.mActivityManager.notifyPinnedStackAnimationStarted();
             } catch (RemoteException e) {
@@ -1561,7 +1556,7 @@
             mService.requestTraversal();
         }
 
-        if (mStackId == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             // Update to the final bounds if requested. This is done here instead of in the bounds
             // animator to allow us to coordinate this after we notify the PiP mode changed
 
@@ -1595,7 +1590,7 @@
      *         bounds and we have a deferred PiP mode changed callback set with the animation.
      */
     public boolean deferScheduleMultiWindowModeChanged() {
-        if (mStackId == PINNED_STACK_ID) {
+        if (inPinnedWindowingMode()) {
             return (mBoundsAnimatingRequested || mBoundsAnimating);
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7213c95..629cc86 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -18,7 +18,7 @@
 
 import com.android.internal.util.ToBooleanFunction;
 
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -447,7 +447,7 @@
 
     private void findWallpaperTarget(DisplayContent dc) {
         mFindResults.reset();
-        if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
+        if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
             // 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/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 6d5673e..9d9805a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -60,7 +60,6 @@
     static final boolean DEBUG_SCREENSHOT = false;
     static final boolean DEBUG_BOOT = false;
     static final boolean DEBUG_LAYOUT_REPEATS = false;
-    static final boolean DEBUG_SURFACE_TRACE = false;
     static final boolean DEBUG_WINDOW_TRACE = false;
     static final boolean DEBUG_TASK_MOVEMENT = false;
     static final boolean DEBUG_TASK_POSITIONING = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1fb2188..4ebcc36 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2384,7 +2384,7 @@
             final Rect insets = new Rect();
             final Rect stableInsets = new Rect();
             Rect surfaceInsets = null;
-            final boolean freeform = win != null && win.inFreeformWorkspace();
+            final boolean freeform = win != null && win.inFreeformWindowingMode();
             if (win != null) {
                 // Containing frame will usually cover the whole screen, including dialog windows.
                 // For freeform workspace windows it will not cover the whole screen and it also
@@ -2794,7 +2794,7 @@
         for (final WindowState win : mWindowMap.values()) {
             final Task task = win.getTask();
             if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1
-                    && task.inFreeformWorkspace()) {
+                    && task.inFreeformWindowingMode()) {
                 final AppWindowToken appToken = win.mAppToken;
                 if (appToken != null && appToken.mAppAnimator != null) {
                     appToken.mAppAnimator.startProlongAnimation(scaleUp ?
@@ -6898,11 +6898,6 @@
                     dumpSessionsLocked(pw, true);
                 }
                 return;
-            } else if ("surfaces".equals(cmd)) {
-                synchronized(mWindowMap) {
-                    WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, null);
-                }
-                return;
             } else if ("displays".equals(cmd) || "d".equals(cmd)) {
                 synchronized(mWindowMap) {
                     mRoot.dumpDisplayContents(pw);
@@ -6967,10 +6962,6 @@
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, dumpAll ?
-                    "-------------------------------------------------------------------------------"
-                    : null);
-            pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
@@ -7604,10 +7595,10 @@
         }
 
         @Override
-        public boolean isStackVisible(int stackId) {
+        public boolean isStackVisible(int windowingMode) {
             synchronized (mWindowMap) {
                 final DisplayContent dc = getDefaultDisplayContentLocked();
-                return dc.isStackVisible(stackId);
+                return dc.isStackVisible(windowingMode);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4ff0f39..2118024 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,10 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -83,7 +80,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -815,13 +811,12 @@
             final WindowState imeWin = mService.mInputMethodWindow;
             // IME is up and obscuring this window. Adjust the window position so it is visible.
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) {
-                final int stackId = getStackId();
-                if (stackId == FREEFORM_WORKSPACE_STACK_ID
+                if (inFreeformWindowingMode()
                         && mContainingFrame.bottom > contentFrame.bottom) {
                     // In freeform we want to move the top up directly.
                     // TODO: Investigate why this is contentFrame not parentFrame.
                     mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
-                } else if (stackId != PINNED_STACK_ID
+                } else if (!inPinnedWindowingMode()
                         && mContainingFrame.bottom > parentFrame.bottom) {
                     // But in docked we want to behave like fullscreen and behave as if the task
                     // were given smaller bounds for the purposes of layout. Skip adjustments for
@@ -898,7 +893,7 @@
             // For pinned workspace the frame isn't limited in any particular
             // way since SystemUI controls the bounds. For freeform however
             // we want to keep things inside the content frame.
-            final Rect limitFrame = task.inPinnedWorkspace() ? mFrame : mContentFrame;
+            final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
             // Keep the frame out of the blocked system area, limit it in size to the content area
             // and make sure that there is always a minimum visible so that the user can drag it
             // into a usable area..
@@ -1210,7 +1205,7 @@
             // application when it has finished drawing.
             if (getOrientationChanging() || dragResizingChanged
                     || isResizedWhileNotDragResizing()) {
-                if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+                if (DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
                     Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
                             + ", mDrawState=DRAW_PENDING in " + this
                             + ", surfaceController " + winAnimator.mSurfaceController);
@@ -1662,9 +1657,9 @@
             //
             // Anyway we don't need to synchronize position and content updates for these
             // windows since they aren't at the base layer and could be moved around anyway.
-            if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION &&
-                    !mWinAnimator.isForceScaled() && !isGoneForLayoutLw() &&
-                    !getTask().inPinnedWorkspace()) {
+            if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION
+                    && !mWinAnimator.isForceScaled() && !isGoneForLayoutLw()
+                    && !getTask().inPinnedWindowingMode()) {
                 setResizedWhileNotDragResizing(true);
             }
         }
@@ -2196,12 +2191,6 @@
         }
     }
 
-    // TODO: Strange usage of word workspace here and above.
-    boolean inPinnedWorkspace() {
-        final Task task = getTask();
-        return task != null && task.inPinnedWorkspace();
-    }
-
     void applyAdjustForImeIfNeeded() {
         final Task task = getTask();
         if (task != null && task.mStack != null && task.mStack.isAdjustedForIme()) {
@@ -2235,7 +2224,7 @@
             } else {
                 getVisibleBounds(mTmpRect);
             }
-            if (inFreeformWorkspace()) {
+            if (inFreeformWindowingMode()) {
                 // For freeform windows we the touch region to include the whole surface for the
                 // shadows.
                 final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
@@ -2938,8 +2927,7 @@
         return mTmpRect;
     }
 
-    @Override
-    public int getStackId() {
+    private int getStackId() {
         final TaskStack stack = getStack();
         if (stack == null) {
             return INVALID_STACK_ID;
@@ -2983,11 +2971,6 @@
         }
     }
 
-    boolean inFreeformWorkspace() {
-        final Task task = getTask();
-        return task != null && task.inFreeformWorkspace();
-    }
-
     @Override
     public boolean isInMultiWindowMode() {
         final Task task = getTask();
@@ -3105,7 +3088,7 @@
         // background.
         return (getDisplayContent().mDividerControllerLocked.isResizing()
                         || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
-                !task.inFreeformWorkspace() && !isGoneForLayoutLw();
+                !task.inFreeformWindowingMode() && !isGoneForLayoutLw();
 
     }
 
@@ -3695,7 +3678,7 @@
 
         // Force the show in the next prepareSurfaceLocked() call.
         mWinAnimator.mLastAlpha = -1;
-        if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) Slog.v(TAG,
+        if (DEBUG_ANIM) Slog.v(TAG,
                 "performShowLocked: mDrawState=HAS_DRAWN in " + this);
         mWinAnimator.mDrawState = HAS_DRAWN;
         mService.scheduleAnimationLocked();
@@ -3756,7 +3739,7 @@
         windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
         windowInfo.focused = isFocused();
         Task task = getTask();
-        windowInfo.inPictureInPicture = (task != null) && task.inPinnedWorkspace();
+        windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
 
         if (mIsChildWindow) {
             windowInfo.parentToken = getParentWindow().mClient.asBinder();
@@ -4219,7 +4202,7 @@
         // If a freeform window is animating from a position where it would be cutoff, it would be
         // cutoff during the animation. We don't want that, so for the duration of the animation
         // we ignore the decor cropping and depend on layering to position windows correctly.
-        final boolean cropToDecor = !(inFreeformWorkspace() && isAnimatingLw());
+        final boolean cropToDecor = !(inFreeformWindowingMode() && isAnimatingLw());
         if (cropToDecor) {
             // Intersect with the decor rect, offsetted by window position.
             systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
@@ -4303,7 +4286,7 @@
         // scale for the animation using the source hint rect
         // (see WindowStateAnimator#setSurfaceBoundariesLocked()).
         if (isDragResizeChanged() || isResizedWhileNotDragResizing()
-                || (surfaceInsetsChanging() && !inPinnedWorkspace())) {
+                || (surfaceInsetsChanging() && !inPinnedWindowingMode())) {
             mLastSurfaceInsets.set(mAttrs.surfaceInsets);
 
             setDragResizing();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1b7e527..5266903 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -31,7 +31,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
@@ -509,7 +508,7 @@
         boolean layoutNeeded = false;
 
         if (mDrawState == DRAW_PENDING) {
-            if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
+            if (DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
                 Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in "
                         + mSurfaceController);
             if (DEBUG_STARTING_WINDOW && startingWindow) {
@@ -532,7 +531,7 @@
         if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
             return false;
         }
-        if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
+        if (DEBUG_ANIM) {
             Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
         }
         mDrawState = READY_TO_SHOW;
@@ -1033,7 +1032,7 @@
                 //Slog.i(TAG_WM, "Not applying alpha transform");
             }
 
-            if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV)
+            if ((DEBUG_ANIM || WindowManagerService.localLOGV)
                     && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
                     TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
                     + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
@@ -1112,7 +1111,7 @@
      */
     private boolean useFinalClipRect() {
         return (isAnimationSet() && resolveStackClip() == STACK_CLIP_AFTER_ANIM)
-                || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWorkspace();
+                || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWindowingMode();
     }
 
     /**
@@ -1177,7 +1176,7 @@
             return false;
         }
 
-        if (w.inPinnedWorkspace()) {
+        if (w.inPinnedWindowingMode()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 2e1e3f7..d56df55 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -22,7 +22,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -565,262 +564,4 @@
     public String toString() {
         return mSurfaceControl.toString();
     }
-
-    static class SurfaceTrace extends SurfaceControl {
-        private final static String SURFACE_TAG = TAG_WITH_CLASS_NAME ? "SurfaceTrace" : TAG_WM;
-        private final static boolean LOG_SURFACE_TRACE = DEBUG_SURFACE_TRACE;
-        final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
-
-        private float mSurfaceTraceAlpha = 0;
-        private int mLayer;
-        private final PointF mPosition = new PointF();
-        private final Point mSize = new Point();
-        private final Rect mWindowCrop = new Rect();
-        private final Rect mFinalCrop = new Rect();
-        private boolean mShown = false;
-        private int mLayerStack;
-        private boolean mIsOpaque;
-        private float mDsdx, mDtdx, mDsdy, mDtdy;
-        private final String mName;
-
-        public SurfaceTrace(SurfaceSession s, String name, int w, int h, int format, int flags,
-                        int windowType, int ownerUid)
-                    throws OutOfResourcesException {
-            super(s, name, w, h, format, flags, windowType, ownerUid);
-            mName = name != null ? name : "Not named";
-            mSize.set(w, h);
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.add(0, this);
-            }
-        }
-
-        public SurfaceTrace(SurfaceSession s,
-                        String name, int w, int h, int format, int flags) {
-            super(s, name, w, h, format, flags);
-            mName = name != null ? name : "Not named";
-            mSize.set(w, h);
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.add(0, this);
-            }
-        }
-
-        @Override
-        public void setAlpha(float alpha) {
-            if (mSurfaceTraceAlpha != alpha) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setAlpha(" + alpha + "): OLD:" + this +
-                        ". Called by " + Debug.getCallers(3));
-                mSurfaceTraceAlpha = alpha;
-            }
-            super.setAlpha(alpha);
-        }
-
-        @Override
-        public void setLayer(int zorder) {
-            if (zorder != mLayer) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayer(" + zorder + "): OLD:" + this
-                        + ". Called by " + Debug.getCallers(3));
-                mLayer = zorder;
-            }
-            super.setLayer(zorder);
-
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-                int i;
-                for (i = sSurfaces.size() - 1; i >= 0; i--) {
-                    SurfaceTrace s = sSurfaces.get(i);
-                    if (s.mLayer < zorder) {
-                        break;
-                    }
-                }
-                sSurfaces.add(i + 1, this);
-            }
-        }
-
-        @Override
-        public void setPosition(float x, float y) {
-            if (x != mPosition.x || y != mPosition.y) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPosition(" + x + "," + y + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mPosition.set(x, y);
-            }
-            super.setPosition(x, y);
-        }
-
-        @Override
-        public void setGeometryAppliesWithResize() {
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setGeometryAppliesWithResize(): OLD: "
-                    + this + ". Called by" + Debug.getCallers(3));
-            super.setGeometryAppliesWithResize();
-        }
-
-        @Override
-        public void setSize(int w, int h) {
-            if (w != mSize.x || h != mSize.y) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mSize.set(w, h);
-            }
-            super.setSize(w, h);
-        }
-
-        @Override
-        public void setWindowCrop(Rect crop) {
-            if (crop != null) {
-                if (!crop.equals(mWindowCrop)) {
-                    if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setWindowCrop("
-                            + crop.toShortString() + "): OLD:" + this + ". Called by "
-                            + Debug.getCallers(3));
-                    mWindowCrop.set(crop);
-                }
-            }
-            super.setWindowCrop(crop);
-        }
-
-        @Override
-        public void setFinalCrop(Rect crop) {
-            if (crop != null) {
-                if (!crop.equals(mFinalCrop)) {
-                    if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop("
-                            + crop.toShortString() + "): OLD:" + this + ". Called by "
-                            + Debug.getCallers(3));
-                    mFinalCrop.set(crop);
-                }
-            }
-            super.setFinalCrop(crop);
-        }
-
-        @Override
-        public void setLayerStack(int layerStack) {
-            if (layerStack != mLayerStack) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mLayerStack = layerStack;
-            }
-            super.setLayerStack(layerStack);
-        }
-
-        @Override
-        public void setOpaque(boolean isOpaque) {
-            if (isOpaque != mIsOpaque) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setOpaque(" + isOpaque + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mIsOpaque = isOpaque;
-            }
-            super.setOpaque(isOpaque);
-        }
-
-        @Override
-        public void setSecure(boolean isSecure) {
-            super.setSecure(isSecure);
-        }
-
-        @Override
-        public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
-            if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + ","
-                        + dsdy + "," + dtdy + "): OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mDsdx = dsdx;
-                mDtdx = dtdx;
-                mDsdy = dsdy;
-                mDtdy = dtdy;
-            }
-            super.setMatrix(dsdx, dtdx, dsdy, dtdy);
-        }
-
-        @Override
-        public void hide() {
-            if (mShown) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "hide: OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mShown = false;
-            }
-            super.hide();
-        }
-
-        @Override
-        public void show() {
-            if (!mShown) {
-                if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "show: OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mShown = true;
-            }
-            super.show();
-        }
-
-        @Override
-        public void destroy() {
-            super.destroy();
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-            }
-        }
-
-        @Override
-        public void release() {
-            super.release();
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "release: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-            }
-        }
-
-        @Override
-        public void setTransparentRegionHint(Region region) {
-            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setTransparentRegionHint(" + region
-                    + "): OLD: " + this + " . Called by " + Debug.getCallers(3));
-            super.setTransparentRegionHint(region);
-        }
-
-        static void dumpAllSurfaces(PrintWriter pw, String header) {
-            synchronized (sSurfaces) {
-                final int N = sSurfaces.size();
-                if (N <= 0) {
-                    return;
-                }
-                if (header != null) {
-                    pw.println(header);
-                }
-                pw.println("WINDOW MANAGER SURFACES (dumpsys window surfaces)");
-                for (int i = 0; i < N; i++) {
-                    SurfaceTrace s = sSurfaces.get(i);
-                    pw.print("  Surface #"); pw.print(i); pw.print(": #");
-                            pw.print(Integer.toHexString(System.identityHashCode(s)));
-                            pw.print(" "); pw.println(s.mName);
-                    pw.print("    mLayerStack="); pw.print(s.mLayerStack);
-                            pw.print(" mLayer="); pw.println(s.mLayer);
-                    pw.print("    mShown="); pw.print(s.mShown); pw.print(" mAlpha=");
-                            pw.print(s.mSurfaceTraceAlpha); pw.print(" mIsOpaque=");
-                            pw.println(s.mIsOpaque);
-                    pw.print("    mPosition="); pw.print(s.mPosition.x); pw.print(",");
-                            pw.print(s.mPosition.y);
-                            pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
-                            pw.println(s.mSize.y);
-                    pw.print("    mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
-                    pw.print("    mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println();
-                    pw.print("    Transform: ("); pw.print(s.mDsdx); pw.print(", ");
-                            pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
-                            pw.print(", "); pw.print(s.mDtdy); pw.println(")");
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
-                    + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
-                    + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
-                    + " " + mSize.x + "x" + mSize.y
-                    + " crop=" + mWindowCrop.toShortString()
-                    + " opaque=" + mIsOpaque
-                    + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
-        }
-    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ff45070..49dd5285 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -125,6 +125,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 
+import static android.os.IServiceManager.DUMP_PRIORITY_CRITICAL;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 public final class SystemServer {
@@ -824,7 +825,8 @@
             wm = WindowManagerService.main(context, inputManager,
                     mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                     !mFirstBoot, mOnlyCore, new PhoneWindowManager());
-            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
+            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
+                    DUMP_PRIORITY_CRITICAL);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
             traceEnd();
 
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 0230f36..1925c39 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -25,6 +25,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.LocalLog;
 
@@ -59,11 +60,14 @@
     private static final boolean DBG = false;
     private static final String MARK_START = "--- START ---";
     private static final String MARK_STOP = "--- STOP ---";
+    private static final String MARK_NAMED_START = "--- START (%s) ---";
+    private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
 
     private final String mTag;
     private final LocalLog mLog;
     private final BlockingSocketReader mPacketListener;
     private boolean mRunning;
+    private String mDisplayName;
 
     public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
         final String ifname;
@@ -85,14 +89,16 @@
         mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
     }
 
-    public void start() {
+    public void start(String displayName) {
         mRunning = true;
+        mDisplayName = displayName;
         mPacketListener.start();
     }
 
     public void stop() {
         mPacketListener.stop();
         mRunning = false;
+        mDisplayName = null;
     }
 
     private final class PacketListener extends BlockingSocketReader {
@@ -133,16 +139,19 @@
 
         @Override
         protected void onStart() {
-            mLog.log(MARK_START);
+            final String msg = TextUtils.isEmpty(mDisplayName)
+                    ? MARK_START
+                    : String.format(MARK_NAMED_START, mDisplayName);
+            mLog.log(msg);
         }
 
         @Override
         protected void onStop() {
-            if (mRunning) {
-                mLog.log(MARK_STOP);
-            } else {
-                mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)");
-            }
+            String msg = TextUtils.isEmpty(mDisplayName)
+                    ? MARK_STOP
+                    : String.format(MARK_NAMED_STOP, mDisplayName);
+            if (!mRunning) msg += " (packet listener stopped unexpectedly)";
+            mLog.log(msg);
         }
 
         @Override
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index bc07b81..e33f6c9 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -26,6 +26,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
@@ -348,6 +349,16 @@
                 return this;
             }
 
+            public Builder withNetwork(Network network) {
+                mConfig.mNetwork = network;
+                return this;
+            }
+
+            public Builder withDisplayName(String displayName) {
+                mConfig.mDisplayName = displayName;
+                return this;
+            }
+
             public ProvisioningConfiguration build() {
                 return new ProvisioningConfiguration(mConfig);
             }
@@ -362,6 +373,8 @@
         /* package */ ApfCapabilities mApfCapabilities;
         /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
         /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
+        /* package */ Network mNetwork = null;
+        /* package */ String mDisplayName = null;
 
         public ProvisioningConfiguration() {} // used by Builder
 
@@ -374,6 +387,9 @@
             mStaticIpConfig = other.mStaticIpConfig;
             mApfCapabilities = other.mApfCapabilities;
             mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
+            mIPv6AddrGenMode = other.mIPv6AddrGenMode;
+            mNetwork = other.mNetwork;
+            mDisplayName = other.mDisplayName;
         }
 
         @Override
@@ -388,6 +404,8 @@
                     .add("mApfCapabilities: " + mApfCapabilities)
                     .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
                     .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
+                    .add("mNetwork: " + mNetwork)
+                    .add("mDisplayName: " + mDisplayName)
                     .toString();
         }
 
@@ -1441,10 +1459,10 @@
         @Override
         public void enter() {
             // Get the Configuration for ApfFilter from Context
-            boolean filter802_3Frames =
+            final boolean filter802_3Frames =
                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
 
-            int[] ethTypeBlackList = mContext.getResources().getIntArray(
+            final int[] ethTypeBlackList = mContext.getResources().getIntArray(
                     R.array.config_apfEthTypeBlackList);
 
             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
@@ -1456,7 +1474,7 @@
             }
 
             mPacketTracker = createPacketTracker();
-            if (mPacketTracker != null) mPacketTracker.start();
+            if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
 
             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
@@ -1470,7 +1488,7 @@
                 return;
             }
 
-            InitialConfiguration config = mConfiguration.mInitialConfig;
+            final InitialConfiguration config = mConfiguration.mInitialConfig;
             if ((config != null) && !applyInitialConfig(config)) {
                 // TODO introduce a new IpManagerEvent constant to distinguish this error case.
                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 343d237..bbd3d13 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -106,6 +106,10 @@
         record(Category.NONE, msg);
     }
 
+    public void logf(String fmt, Object... args) {
+        log(String.format(fmt, args));
+    }
+
     public void mark(String msg) {
         record(Category.MARK, msg);
     }
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 9fa1d68..0b4d61f 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -19,6 +19,7 @@
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
 
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -57,7 +58,13 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.util.IntPair;
 import com.android.server.lights.Light;
 
 import org.junit.Before;
@@ -67,6 +74,8 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -80,6 +89,8 @@
     NotificationManagerService.WorkerHandler mHandler;
     @Mock
     NotificationUsageStats mUsageStats;
+    @Mock
+    IAccessibilityManager mAccessibilityService;
 
     private NotificationManagerService mService;
     private String mPkg = "com.android.server.notification";
@@ -111,17 +122,25 @@
     private static final int MAX_VIBRATION_DELAY = 1000;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
         when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
 
-        mService = new NotificationManagerService(getContext());
+        long serviceReturnValue = IntPair.of(
+                AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
+                AccessibilityEvent.TYPES_ALL_MASK);
+        when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue);
+        AccessibilityManager accessibilityManager =
+                new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0);
+        verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
+        assertTrue(accessibilityManager.isEnabled());
+
+        mService = spy(new NotificationManagerService(getContext()));
         mService.setAudioManager(mAudioManager);
         mService.setVibrator(mVibrator);
         mService.setSystemReady(true);
@@ -130,6 +149,7 @@
         mService.setScreenOn(false);
         mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
         mService.setUsageStats(mUsageStats);
+        mService.setAccessibilityManager(accessibilityManager);
     }
 
     //
@@ -381,6 +401,7 @@
 
         verifyBeepLooped();
         verifyNeverVibrate();
+        verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
     }
 
     @Test
@@ -435,6 +456,7 @@
         r.isUpdate = true;
         mService.buzzBeepBlinkLocked(r);
         verifyBeepLooped();
+        verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
     }
 
     @Test
@@ -450,6 +472,7 @@
         // update should not beep
         mService.buzzBeepBlinkLocked(s);
         verifyNeverBeep();
+        verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
     }
 
     @Test
@@ -547,7 +570,7 @@
         mService.mInCall = true;
         mService.buzzBeepBlinkLocked(r);
 
-        //verify(mService, times(1)).playInCallNotification();
+        verify(mService, times(1)).playInCallNotification();
         verifyNeverBeep(); // doesn't play normal beep
     }
 
@@ -842,7 +865,6 @@
         mService.addNotification(r);
 
         mService.buzzBeepBlinkLocked(r);
-
         verifyNeverBeep();
     }
 
@@ -870,7 +892,6 @@
         summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
 
         mService.buzzBeepBlinkLocked(summary);
-
         verify(mUsageStats, never()).isAlertRateLimited(any());
     }
 
@@ -889,6 +910,30 @@
         verifyNeverBeep();
     }
 
+    @Test
+    public void testA11yMinInitialPost() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        r.setImportance(IMPORTANCE_MIN, "");
+        mService.buzzBeepBlinkLocked(r);
+        verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
+    public void testA11yQuietInitialPost() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
+    public void testA11yQuietUpdate() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        mService.buzzBeepBlinkLocked(r);
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+        verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
new file mode 100644
index 0000000..cbe9650
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.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 distriZenbuted on an "AS IS" BASIS,
+ * WITHOUT 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.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.media.AudioAttributes;
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ZenModeHelperTest extends NotificationTestCase {
+
+    @Mock ConditionProviders mConditionProviders;
+    private TestableLooper mTestableLooper;
+    private ZenModeHelper mZenModeHelperSpy;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+        mZenModeHelperSpy = spy(new ZenModeHelper(getContext(), mTestableLooper.getLooper(),
+                mConditionProviders));
+    }
+
+    @Test
+    public void testZenOff_NoMuteApplied() {
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF;
+        assertTrue(mZenModeHelperSpy.mConfig.allowAlarms);
+        mZenModeHelperSpy.applyRestrictions();
+
+        doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt());
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_ALARM);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_MEDIA);
+    }
+
+    @Test
+    public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        assertTrue(mZenModeHelperSpy.mConfig.allowAlarms);
+        assertTrue(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+        mZenModeHelperSpy.applyRestrictions();
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_ALARM);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_MEDIA);
+    }
+
+    @Test
+    public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
+
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mConfig.allowAlarms = false;
+        mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+        assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+        mZenModeHelperSpy.applyRestrictions();
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_ALARM);
+
+        // Media is a catch-all that includes games and system sounds
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_MEDIA);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_GAME);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+    }
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index fd83223..6d7d4cb 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -62,6 +62,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
 
+LOCAL_PROGUARD_ENABLED := disabled
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/assets/shortcut/shortcut_api27_backup.xml b/services/tests/servicestests/assets/shortcut/shortcut_api27_backup.xml
new file mode 100644
index 0000000..266ab99
--- /dev/null
+++ b/services/tests/servicestests/assets/shortcut/shortcut_api27_backup.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<user>
+    <launcher-pins package-name="com.android.launcher.1" launcher-user="0">
+        <package-info version="9" last_udpate_time="1230768000000">
+            <signature hash="0X8l7PvMeFf3vr6kaTCL4LJYCUPpbROjrZihNnXEv8I" />
+        </package-info>
+        <package package-name="com.android.test.1" package-user="0">
+            <pin value="s1" />
+        </package>
+    </launcher-pins>
+    <package name="com.android.test.1" call-count="0" last-reset="1506991428317">
+        <package-info version="8" last_udpate_time="1506534668998">
+            <signature hash="zDmdc5A/Bu5pQDKrBTjwVjT/fhzl6OUKwzCocUhPNM8" />
+        </package-info>
+        <shortcut id="s1" activity="com.android.test.1/com.example.android.shortcutsample.Main" title="Leak wakelock" titleid="0" textid="0" dmessageid="0" timestamp="1507674156622" flags="130">
+            <intent intent-base="#Intent;action=com.example.android.shortcutsample.LEAK;launchFlags=0x1000c000;component=com.example.android.shortcutsample/.Main;end" />
+        </shortcut>
+    </package>
+</user>
diff --git a/services/tests/servicestests/res/xml/shortcut_5_altalt.xml b/services/tests/servicestests/res/xml/shortcut_5_altalt.xml
new file mode 100644
index 0000000..1476a27
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_5_altalt.xml
@@ -0,0 +1,87 @@
+<?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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+    <shortcut
+        android:shortcutId="ms1"
+        android:enabled="true"
+        android:icon="@drawable/icon1"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        android:shortcutLongLabel="@string/shortcut_text1"
+        android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+        >
+        <intent
+            android:action="action1"
+            android:data="http://a.b.c/1"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
+    <shortcut
+        android:shortcutId="ms2"
+        android:enabled="true"
+        android:icon="@drawable/icon2"
+        android:shortcutShortLabel="@string/shortcut_title2"
+        android:shortcutLongLabel="@string/shortcut_text2"
+        android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+        >
+        <intent
+            android:action="action2"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
+    <shortcut
+        android:shortcutId="ms3"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        android:shortcutLongLabel="@string/shortcut_title2"
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
+    <shortcut
+        android:shortcutId="ms4"
+        android:shortcutShortLabel="@string/shortcut_title2"
+        android:shortcutLongLabel="@string/shortcut_title2"
+        >
+        <intent
+            android:action="android.intent.action.VIEW2"
+            >
+        </intent>
+        <categories />
+        <categories android:name="" />
+        <categories android:name="cat" />
+    </shortcut>
+    <shortcut
+        android:shortcutId="ms5"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        >
+        <intent
+            android:action="action"
+            android:data="http://www/"
+            android:targetPackage="abc"
+            android:targetClass=".xyz"
+            android:mimeType="foo/bar"
+            >
+            <categories android:name="cat1" />
+            <categories android:name="cat2" />
+            <extra android:name="key1" android:value="value1" />
+            <extra android:name="key2" android:value="value2" />
+        </intent>
+    </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
index 02f645a..c8dc9ff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
@@ -77,7 +77,6 @@
         mAccessibilityCache.clear();
         AccessibilityInteractionClient.getInstance().clearCache();
         assertEquals(0, numA11yWinInfosInUse.get());
-        assertEquals(0, numA11yNodeInfosInUse.get());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 855b82c..e1623b0 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -83,7 +82,7 @@
         assertTrue(task2.getStack() instanceof PinnedActivityStack);
         mStarter.updateBounds(task2, bounds);
 
-        verify(mService, times(1)).resizeStack(eq(ActivityManager.StackId.PINNED_STACK_ID),
+        verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
                 eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
 
         // In the case of no animation, the stack and task bounds should be set immediately.
@@ -94,4 +93,4 @@
             assertEquals(task2.mBounds, null);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 1f6dda1..20077f3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -55,10 +55,6 @@
     private final Context mContext = InstrumentationRegistry.getContext();
     private HandlerThread mHandlerThread;
 
-    // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must
-    // be called at before any tests.
-    private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext);
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -107,15 +103,21 @@
 
     protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
             ComponentName component, ActivityStack stack) {
+        return createTask(supervisor, component, 0 /* flags */, 0 /* taskId */, stack);
+    }
+
+    protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
+            ComponentName component, int flags, int taskId, ActivityStack stack) {
         final ActivityInfo aInfo = new ActivityInfo();
         aInfo.applicationInfo = new ApplicationInfo();
         aInfo.applicationInfo.packageName = component.getPackageName();
 
         Intent intent = new Intent();
         intent.setComponent(component);
+        intent.setFlags(flags);
 
-        final TaskRecord task = new TaskRecord(supervisor.mService, 0, aInfo, intent /*intent*/,
-                null /*_taskDescription*/);
+        final TaskRecord task = new TaskRecord(supervisor.mService, taskId, aInfo,
+                intent /*intent*/, null /*_taskDescription*/);
         supervisor.setFocusStackUnchecked("test", stack);
         stack.addTask(task, true, "creating test task");
         task.setStack(stack);
@@ -136,7 +138,7 @@
             mSupportsSplitScreenMultiWindow = true;
             mSupportsFreeformWindowManagement = true;
             mSupportsPictureInPicture = true;
-            mWindowManager = WindowTestUtils.getWindowManagerService(context);
+            mWindowManager = WindowTestUtils.getMockWindowManagerService();
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java
new file mode 100644
index 0000000..e6d6831
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java
@@ -0,0 +1,245 @@
+/*
+ * 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.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.view.Display;
+import android.view.Gravity;
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.ArrayList;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doAnswer;
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Tests for exercising resizing bounds.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.LaunchBoundsTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LaunchBoundsTests extends ActivityTestsBase {
+    private final ComponentName testActivityComponent =
+            ComponentName.unflattenFromString("com.foo/.BarActivity");
+    private final ComponentName testActivityComponent2 =
+            ComponentName.unflattenFromString("com.foo/.BarActivity2");
+
+    private final static int STACK_WIDTH = 100;
+    private final static int STACK_HEIGHT = 200;
+
+    private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
+
+    private ActivityManagerService mService;
+    private ActivityStack mStack;
+    private TaskRecord mTask;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mService = createActivityManagerService();
+        mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mStack.requestResize(STACK_BOUNDS);
+
+        // We must create the task after resizing to make sure it does not inherit the stack
+        // dimensions on resize.
+        mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+    }
+
+    /**
+     * Ensures that the setup bounds are set as expected with the stack bounds set and the task
+     * bounds still {@code null}.
+     * @throws Exception
+     */
+    @Test
+    public void testInitialBounds() throws Exception {
+        assertEquals(mStack.mBounds, STACK_BOUNDS);
+        assertEquals(mTask.mBounds, null);
+    }
+
+    /**
+     * Ensures that a task positioned with no {@link WindowLayout} receives the default launch
+     * position.
+     * @throws Exception
+     */
+    @Test
+    public void testLaunchNoWindowLayout() throws Exception {
+        final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
+
+        mStack.layoutTaskInStack(mTask, null);
+
+        // We expect the task to be placed in the middle of the screen with margins applied.
+        assertEquals(mTask.mBounds, expectedTaskBounds);
+    }
+
+    /**
+     * Ensures that a task positioned with an empty {@link WindowLayout} receives the default launch
+     * position.
+     * @throws Exception
+     */
+    @Test
+    public void testlaunchEmptyWindowLayout() throws Exception {
+        final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
+
+        WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
+        mStack.layoutTaskInStack(mTask, layout);
+        assertEquals(mTask.mBounds, expectedTaskBounds);
+    }
+
+    /**
+     * Ensures that a task positioned with a {@link WindowLayout} gravity specified is positioned
+     * according to specification.
+     * @throws Exception
+     */
+    @Test
+    public void testlaunchWindowLayoutGravity() throws Exception {
+        // Unspecified gravity should be ignored
+        testGravity(Gravity.NO_GRAVITY);
+
+        // Unsupported gravity should be ignored
+        testGravity(Gravity.LEFT);
+        testGravity(Gravity.RIGHT);
+
+        // Test defaults for vertical gravities
+        testGravity(Gravity.TOP);
+        testGravity(Gravity.BOTTOM);
+
+        // Test corners
+        testGravity(Gravity.TOP | Gravity.LEFT);
+        testGravity(Gravity.TOP | Gravity.RIGHT);
+        testGravity(Gravity.BOTTOM | Gravity.LEFT);
+        testGravity(Gravity.BOTTOM | Gravity.RIGHT);
+    }
+
+    private void testGravity(int gravity) {
+        final WindowLayout gravityLayout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
+        mStack.layoutTaskInStack(mTask, gravityLayout);
+        assertEquals(mTask.mBounds, getDefaultBounds(gravity));
+    }
+
+    /**
+     * Ensures that a task which causes a conflict with another task when positioned is adjusted as
+     * expected.
+     * @throws Exception
+     */
+    @Test
+    public void testLaunchWindowCenterConflict() throws Exception {
+        testConflict(Gravity.NO_GRAVITY);
+        testConflict(Gravity.TOP);
+        testConflict(Gravity.BOTTOM);
+        testConflict(Gravity.TOP | Gravity.LEFT);
+        testConflict(Gravity.TOP | Gravity.RIGHT);
+        testConflict(Gravity.BOTTOM | Gravity.LEFT);
+        testConflict(Gravity.BOTTOM | Gravity.RIGHT);
+    }
+
+    private void testConflict(int gravity) {
+        final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
+
+        // layout first task
+        mStack.layoutTaskInStack(mTask, layout /*windowLayout*/);
+
+        // Second task will be laid out on top of the first so starting bounds is the same.
+        final Rect expectedBounds = new Rect(mTask.mBounds);
+
+        ActivityRecord activity = null;
+        TaskRecord secondTask = null;
+
+        // wrap with try/finally to ensure cleanup of activity/stack.
+        try {
+            // empty tasks are ignored in conflicts
+            activity = createActivity(mService, testActivityComponent, mTask);
+
+            // Create secondary task
+            secondTask = createTask(mService.mStackSupervisor, testActivityComponent,
+                    mStack);
+
+            // layout second task
+            mStack.layoutTaskInStack(secondTask, layout /*windowLayout*/);
+
+            if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
+                    || (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
+                    == (Gravity.BOTTOM | Gravity.RIGHT)) {
+                expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
+                        0);
+            } else if ((gravity & Gravity.TOP) == Gravity.TOP
+                    || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+                expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), 0);
+            } else {
+                expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
+                        LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
+            }
+
+            assertEquals(secondTask.mBounds, expectedBounds);
+        } finally {
+            // Remove task and activity to prevent influencing future tests
+            if (activity != null) {
+                mTask.removeActivity(activity);
+            }
+
+            if (secondTask != null) {
+                mStack.removeTask(secondTask, "cleanup", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+    }
+
+    private Rect getDefaultBounds(int gravity) {
+        final Rect bounds = new Rect();
+        bounds.set(mStack.mBounds);
+
+        final int verticalInset = LaunchingTaskPositioner.getFreeformStartTop(mStack.mBounds);
+        final int horizontalInset = LaunchingTaskPositioner.getFreeformStartLeft(mStack.mBounds);
+
+        bounds.inset(horizontalInset, verticalInset);
+
+        if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)) {
+            bounds.offsetTo(horizontalInset * 2, 0);
+        } else if ((gravity & Gravity.TOP) == Gravity.TOP) {
+            bounds.offsetTo(0, 0);
+        } else if ((gravity & (Gravity.BOTTOM | Gravity.RIGHT))
+                == (Gravity.BOTTOM | Gravity.RIGHT)) {
+            bounds.offsetTo(horizontalInset * 2, verticalInset * 2);
+        } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+            bounds.offsetTo(0, verticalInset * 2);
+        }
+
+        return bounds;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
new file mode 100644
index 0000000..e607228
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.MutableLong;
+import android.util.SparseBooleanArray;
+
+import com.android.server.am.RecentTasks.Callbacks;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RecentTasksTest extends ActivityTestsBase {
+    private static final int TEST_USER_0_ID = 0;
+    private static final int TEST_USER_1_ID = 10;
+    private static final int TEST_QUIET_USER_ID = 20;
+    private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
+    private static final UserInfo QUIET_USER_INFO = new UserInfo();
+    private static int LAST_TASK_ID = 1;
+
+    private Context mContext = InstrumentationRegistry.getContext();
+    private ActivityManagerService mService;
+    private ActivityStack mStack;
+    private TestTaskPersister mTaskPersister;
+    private RecentTasks mRecentTasks;
+
+    private static ArrayList<TaskRecord> mTasks = new ArrayList<>();
+    private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>();
+
+    private CallbacksRecorder mCallbacksRecorder;
+
+    class TestUserController extends UserController {
+        TestUserController(ActivityManagerService service) {
+            super(service);
+        }
+
+        @Override
+        int[] getCurrentProfileIds() {
+            return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
+        }
+
+        @Override
+        UserInfo getUserInfo(int userId) {
+            switch (userId) {
+                case TEST_USER_0_ID:
+                case TEST_USER_1_ID:
+                    return DEFAULT_USER_INFO;
+                case TEST_QUIET_USER_ID:
+                    return QUIET_USER_INFO;
+            }
+            return null;
+        }
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mService = createActivityManagerService();
+        mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
+        mRecentTasks = new RecentTasks(mService, mTaskPersister, new TestUserController(mService));
+        mRecentTasks.loadParametersFromResources(mContext.getResources());
+        mCallbacksRecorder = new CallbacksRecorder();
+        mRecentTasks.registerCallback(mCallbacksRecorder);
+        QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
+
+        mTasks.add(createTask(".Task1"));
+        mTasks.add(createTask(".Task2"));
+        mTasks.add(createTask(".Task3"));
+        mTasks.add(createTask(".Task4"));
+        mTasks.add(createTask(".Task5"));
+
+        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */));
+        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */));
+    }
+
+    @Test
+    public void testCallbacks() throws Exception {
+        // Add some tasks
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        assertTrue(mCallbacksRecorder.added.contains(mTasks.get(0))
+                && mCallbacksRecorder.added.contains(mTasks.get(1)));
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.isEmpty());
+        mCallbacksRecorder.clear();
+
+        // Remove some tasks
+        mRecentTasks.remove(mTasks.get(0));
+        mRecentTasks.remove(mTasks.get(1));
+        assertTrue(mCallbacksRecorder.added.isEmpty());
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(0)));
+        assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1)));
+        mCallbacksRecorder.clear();
+
+        // Add a task which will trigger the trimming of another
+        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1", null /* affinity */);
+        documentTask1.maxRecents = 1;
+        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1", null /* affinity */);
+        mRecentTasks.add(documentTask1);
+        mRecentTasks.add(documentTask2);
+        assertTrue(mCallbacksRecorder.added.contains(documentTask1));
+        assertTrue(mCallbacksRecorder.added.contains(documentTask2));
+        assertTrue(mCallbacksRecorder.trimmed.contains(documentTask1));
+        assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
+        mCallbacksRecorder.clear();
+
+        // Remove the callback, ensure we don't get any calls
+        mRecentTasks.unregisterCallback(mCallbacksRecorder);
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.remove(mTasks.get(0));
+        assertTrue(mCallbacksRecorder.added.isEmpty());
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.isEmpty());
+    }
+
+    @Test
+    public void testUsersTasks() throws Exception {
+        // Setup some tasks for the users
+        mTaskPersister.userTaskIdsOverride = new SparseBooleanArray();
+        mTaskPersister.userTaskIdsOverride.put(1, true);
+        mTaskPersister.userTaskIdsOverride.put(2, true);
+        mTaskPersister.userTasksOverride = new ArrayList<>();
+        mTaskPersister.userTasksOverride.add(createTask(".UserTask1"));
+        mTaskPersister.userTasksOverride.add(createTask(".UserTask2"));
+
+        // Assert no user tasks are initially loaded
+        assertTrue(mRecentTasks.usersWithRecentsLoadedLocked().length == 0);
+
+        // Load user 0 tasks
+        mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+
+        // Load user 1 tasks
+        mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID);
+        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
+        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID));
+
+        // Unload user 1 tasks
+        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID);
+        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
+        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+
+        // Unload user 0 tasks
+        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
+        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
+        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
+    }
+
+    @Test
+    public void testOrderedIteration() throws Exception {
+        MutableLong prevLastActiveTime = new MutableLong(0);
+        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
+        for (int i = 0; i < tasks.size(); i++) {
+            final TaskRecord task = tasks.get(i);
+            assertTrue(task.lastActiveTime >= prevLastActiveTime.value);
+            prevLastActiveTime.value = task.lastActiveTime;
+        }
+    }
+
+    @Test
+    public void testTrimToGlobalMaxNumRecents() throws Exception {
+        // Limit the global maximum number of recent tasks to a fixed size
+        mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */);
+
+        // Add N+1 tasks
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+
+        // Ensure that the last task was trimmed as an inactive task
+        assertTrimmed(mTasks.get(0));
+    }
+
+    @Test
+    public void testTrimQuietProfileTasks() throws Exception {
+        TaskRecord qt1 = createTask(".QuietTask1", TEST_QUIET_USER_ID);
+        TaskRecord qt2 = createTask(".QuietTask2", TEST_QUIET_USER_ID);
+        mRecentTasks.add(qt1);
+        mRecentTasks.add(qt2);
+
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+
+        // Ensure that the quiet user's tasks was trimmed once the new tasks were added
+        assertTrimmed(qt1, qt2);
+    }
+
+    @Test
+    public void testSessionDuration() throws Exception {
+        mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */);
+
+        TaskRecord t1 = createTask(".Task1");
+        t1.touchActiveTime();
+        mRecentTasks.add(t1);
+
+        // Force a small sleep just beyond the session duration
+        SystemClock.sleep(75);
+
+        TaskRecord t2 = createTask(".Task2");
+        t2.touchActiveTime();
+        mRecentTasks.add(t2);
+
+        // Assert that the old task has been removed due to being out of the active session
+        assertTrimmed(t1);
+    }
+
+    @Test
+    public void testVisibleTasks_excludedFromRecents() throws Exception {
+        mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
+
+        TaskRecord excludedTask1 = createTask(".ExcludedTask1", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
+                TEST_USER_0_ID);
+        TaskRecord excludedTask2 = createTask(".ExcludedTask2", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
+                TEST_USER_0_ID);
+
+        mRecentTasks.add(excludedTask1);
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+        mRecentTasks.add(excludedTask2);
+
+        // The last excluded task should be trimmed, while the first-most excluded task should not
+        assertTrimmed(excludedTask1);
+    }
+
+    @Test
+    public void testVisibleTasks_minNum() throws Exception {
+        mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */);
+
+        for (int i = 0; i < 4; i++) {
+            final TaskRecord task = mTasks.get(i);
+            task.touchActiveTime();
+            mRecentTasks.add(task);
+        }
+
+        // Force a small sleep just beyond the session duration
+        SystemClock.sleep(50);
+
+        // Add a new task to trigger tasks to be trimmed
+        mRecentTasks.add(mTasks.get(4));
+
+        // Ensure that there are a minimum number of tasks regardless of session length
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.isEmpty());
+    }
+
+    @Test
+    public void testVisibleTasks_maxNum() throws Exception {
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
+
+        for (int i = 0; i < 5; i++) {
+            final TaskRecord task = mTasks.get(i);
+            task.touchActiveTime();
+            mRecentTasks.add(task);
+        }
+
+        // Ensure that only the last number of max tasks are kept
+        assertTrimmed(mTasks.get(0), mTasks.get(1));
+    }
+
+    private ComponentName createComponent(String className) {
+        return new ComponentName(mContext.getPackageName(), className);
+    }
+
+    private TaskRecord createTask(String className) {
+        return createTask(className, TEST_USER_0_ID);
+    }
+
+    private TaskRecord createTask(String className, int userId) {
+        return createTask(className, 0 /* flags */, userId);
+    }
+
+    private TaskRecord createTask(String className, int flags, int userId) {
+        TaskRecord task = createTask(mService.mStackSupervisor, createComponent(className), flags,
+                LAST_TASK_ID++, mStack);
+        task.userId = userId;
+        task.touchActiveTime();
+        return task;
+    }
+
+    private TaskRecord createDocumentTask(String className, String affinity) {
+        TaskRecord task = createTask(className, FLAG_ACTIVITY_NEW_DOCUMENT, TEST_USER_0_ID);
+        task.affinity = affinity;
+        return task;
+    }
+
+    private boolean arrayContainsUser(int[] userIds, int targetUserId) {
+        Arrays.sort(userIds);
+        return Arrays.binarySearch(userIds, targetUserId) >= 0;
+    }
+
+    private void assertTrimmed(TaskRecord... tasks) {
+        final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.trimmed;
+        final ArrayList<TaskRecord> removed = mCallbacksRecorder.removed;
+        assertTrue("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size(),
+                trimmed.size() == tasks.length);
+        assertTrue("Expected " + tasks.length + " removed tasks, got " + removed.size(),
+                removed.size() == tasks.length);
+        for (TaskRecord task : tasks) {
+            assertTrue("Expected trimmed task: " + task, trimmed.contains(task));
+            assertTrue("Expected removed task: " + task, removed.contains(task));
+        }
+    }
+
+    private static class CallbacksRecorder implements Callbacks {
+        ArrayList<TaskRecord> added = new ArrayList<>();
+        ArrayList<TaskRecord> trimmed = new ArrayList<>();
+        ArrayList<TaskRecord> removed = new ArrayList<>();
+
+        void clear() {
+            added.clear();
+            trimmed.clear();
+            removed.clear();
+        }
+
+        @Override
+        public void onRecentTaskAdded(TaskRecord task) {
+            added.add(task);
+        }
+
+        @Override
+        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+            if (wasTrimmed) {
+                trimmed.add(task);
+            }
+            removed.add(task);
+        }
+    }
+
+    private static class TestTaskPersister extends TaskPersister {
+
+        SparseBooleanArray userTaskIdsOverride;
+        ArrayList<TaskRecord> userTasksOverride;
+
+        TestTaskPersister(File workingDir) {
+            super(workingDir);
+        }
+
+        @Override
+        SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
+            if (userTaskIdsOverride != null) {
+                return userTaskIdsOverride;
+            }
+            return super.loadPersistedTaskIdsForUser(userId);
+        }
+
+        @Override
+        List<TaskRecord> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) {
+            if (userTasksOverride != null) {
+                return userTasksOverride;
+            }
+            return super.restoreTasksForUserLocked(userId, preaddedTasks);
+        }
+    }
+}
\ 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 f4c4ea9..1bb93cc 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -247,6 +247,26 @@
         assertEquals(7, updates.size());
     }
 
+    public void testUpdatesReceived_queueEmptyAfterStartListening() {
+        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());
+
+        // Stop and start listening again
+        mService.stopListening(mPkgName, HOST_ID);
+        updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[]{widgetId, widgetId2}).getList();
+        assertTrue(updates.isEmpty());
+    }
+
     public void testGetInstalledProvidersForPackage() {
         List<AppWidgetProviderInfo> allProviders = mManager.getInstalledProviders();
         assertTrue(!allProviders.isEmpty());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 951f226..d017976 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -15,6 +15,11 @@
  */
 package com.android.server.pm;
 
+import static android.content.pm.ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
+import static android.content.pm.ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
+import static android.content.pm.ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
+import static android.content.pm.ShortcutInfo.DISABLED_REASON_VERSION_LOWER;
+
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyOrNull;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyStringOrNull;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
@@ -71,7 +76,9 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
@@ -101,7 +108,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
 
 /**
  * Tests for ShortcutService and ShortcutManager.
@@ -250,7 +256,7 @@
         final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.icon2));
         final Icon icon3 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
-            getTestContext().getResources(), R.drawable.icon2));
+                getTestContext().getResources(), R.drawable.icon2));
 
         final ShortcutInfo si1 = makeShortcut(
                 "shortcut1",
@@ -706,13 +712,13 @@
         assertBitmapSize(128, 128, bmp);
 
         Drawable dr = mLauncherApps.getShortcutIconDrawable(
-            makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0);
+                makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0);
         assertTrue(dr instanceof AdaptiveIconDrawable);
         float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
         assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
-            dr.getIntrinsicWidth());
+                dr.getIntrinsicWidth());
         assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
-            dr.getIntrinsicHeight());
+                dr.getIntrinsicHeight());
     }
 
     public void testCleanupDanglingBitmaps() throws Exception {
@@ -1118,8 +1124,8 @@
             // Set resource icon
             assertTrue(mManager.updateShortcuts(list(
                     new ShortcutInfo.Builder(mClientContext, "s1")
-                    .setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
-                    .build()
+                            .setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
+                            .build()
             )));
 
             assertWith(getCallerShortcuts())
@@ -1131,9 +1137,9 @@
             // Set bitmap icon
             assertTrue(mManager.updateShortcuts(list(
                     new ShortcutInfo.Builder(mClientContext, "s1")
-                    .setIcon(Icon.createWithBitmap(BitmapFactory.decodeResource(
-                            getTestContext().getResources(), R.drawable.black_64x64)))
-                    .build()
+                            .setIcon(Icon.createWithBitmap(BitmapFactory.decodeResource(
+                                    getTestContext().getResources(), R.drawable.black_64x64)))
+                            .build()
             )));
 
             assertWith(getCallerShortcuts())
@@ -2126,7 +2132,7 @@
 
             final ShortcutQuery q = new ShortcutQuery().setQueryFlags(
                     ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_PINNED
-                    | ShortcutQuery.FLAG_MATCH_MANIFEST);
+                            | ShortcutQuery.FLAG_MATCH_MANIFEST);
 
             // No shortcuts are visible.
             assertWith(mLauncherApps.getShortcuts(q, HANDLE_USER_0)).isEmpty();
@@ -2668,10 +2674,10 @@
                     "Title 1",
                     makeComponent(ShortcutActivity.class),
                     /* icon =*/ null,
-                    new Intent[] {makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                    new Intent[]{makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
                             "key1", "val1", "nest", makeBundle("key", 123))
                             .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
-                    new Intent("act2").setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)},
+                            new Intent("act2").setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)},
                     /* rank */ 10);
 
             final ShortcutInfo s1_2 = makeShortcut(
@@ -2776,8 +2782,8 @@
             // Not launchable.
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
                     .when(mMockActivityManagerInternal).startActivitiesAsPackage(
-                            anyStringOrNull(), anyInt(),
-                            anyOrNull(Intent[].class), anyOrNull(Bundle.class));
+                    anyStringOrNull(), anyInt(),
+                    anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
 
@@ -2911,7 +2917,7 @@
                     .revertToOriginalList()
                     .selectByIds("s1", "s2")
                     .areAllDynamic()
-                    ;
+            ;
         });
 
         // 3 Update the app with no manifest shortcuts.  (Pinned one will survive.)
@@ -3309,13 +3315,13 @@
         });
 
 
-        final SparseArray<ShortcutUser> users =  mService.getShortcutsForTest();
+        final SparseArray<ShortcutUser> users = mService.getShortcutsForTest();
         assertEquals(2, users.size());
         assertEquals(USER_0, users.keyAt(0));
         assertEquals(USER_10, users.keyAt(1));
 
-        final ShortcutUser user0 =  users.get(USER_0);
-        final ShortcutUser user10 =  users.get(USER_10);
+        final ShortcutUser user0 = users.get(USER_0);
+        final ShortcutUser user10 = users.get(USER_10);
 
 
         // Check the registered packages.
@@ -3531,7 +3537,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -3548,7 +3554,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -3853,52 +3859,77 @@
         assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
     }
 
-    protected void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi,
-            int version, String... signatures) {
-        assertEquals(expected, spi.canRestoreTo(mService, genPackage(
-                "dummy", /* uid */ 0, version, signatures)));
+    protected void checkCanRestoreTo(int expected, ShortcutPackageInfo spi,
+            boolean anyVersionOk, int version, boolean nowBackupAllowed, String... signatures) {
+        final PackageInfo pi = genPackage("dummy", /* uid */ 0, version, signatures);
+        if (!nowBackupAllowed) {
+            pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP;
+        }
+        assertEquals(expected, spi.canRestoreTo(mService, pi, anyVersionOk));
     }
 
     public void testCanRestoreTo() {
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
-        addPackage(CALLING_PACKAGE_2, CALLING_UID_1, 10, "sig1", "sig2");
+        addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
+        addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
+
+        updatePackageInfo(CALLING_PACKAGE_3,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
 
         final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackageForTest(
                 mService, CALLING_PACKAGE_1, USER_0);
         final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackageForTest(
                 mService, CALLING_PACKAGE_2, USER_0);
+        final ShortcutPackageInfo spi3 = ShortcutPackageInfo.generateForInstalledPackageForTest(
+                mService, CALLING_PACKAGE_3, USER_0);
 
-        checkCanRestoreTo(true, spi1, 10, "sig1");
-        checkCanRestoreTo(true, spi1, 10, "x", "sig1");
-        checkCanRestoreTo(true, spi1, 10, "sig1", "y");
-        checkCanRestoreTo(true, spi1, 10, "x", "sig1", "y");
-        checkCanRestoreTo(true, spi1, 11, "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, false, 10, true, "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, false, 10, true, "x", "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, false, 10, true, "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, false, 10, true, "x", "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, false, 11, true, "sig1");
 
-        checkCanRestoreTo(false, spi1, 10 /* empty */);
-        checkCanRestoreTo(false, spi1, 10, "x");
-        checkCanRestoreTo(false, spi1, 10, "x", "y");
-        checkCanRestoreTo(false, spi1, 10, "x");
-        checkCanRestoreTo(false, spi1, 9, "sig1");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true/* empty */);
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x", "y");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
+        checkCanRestoreTo(DISABLED_REASON_VERSION_LOWER, spi1, false, 9, true, "sig1");
 
-        checkCanRestoreTo(true, spi2, 10, "sig1", "sig2");
-        checkCanRestoreTo(true, spi2, 10, "sig2", "sig1");
-        checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2");
-        checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1");
-        checkCanRestoreTo(true, spi2, 10, "sig1", "sig2", "y");
-        checkCanRestoreTo(true, spi2, 10, "sig2", "sig1", "y");
-        checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2", "y");
-        checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1", "y");
-        checkCanRestoreTo(true, spi2, 11, "x", "sig2", "sig1", "y");
+        // Any version okay.
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, true, 9, true, "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, true, 9, true, "sig1");
 
-        checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x");
-        checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x");
-        checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2");
-        checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1");
-        checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x", "y");
-        checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x", "y");
-        checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2", "y");
-        checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1", "y");
-        checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "sig1", "sig2");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "sig2", "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "x", "sig1", "sig2");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "x", "sig2", "sig1");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "sig1", "sig2", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "sig2", "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "x", "sig1", "sig2", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 10, true, "x", "sig2", "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi2, false, 11, true, "x", "sig2", "sig1", "y");
+
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "sig1", "sig2x");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "sig2", "sig1x");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "x", "sig1x", "sig2");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "x", "sig2x", "sig1");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "sig1", "sig2x", "y");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "sig2", "sig1x", "y");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "x", "sig1x", "sig2", "y");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 10, true, "x", "sig2x", "sig1", "y");
+        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH,
+                spi2, false, 11, true, "x", "sig2x", "sig1", "y");
+
+        checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi1, true, 10, false, "sig1");
+        checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1");
     }
 
     public void testHandlePackageDelete() {
@@ -3929,7 +3960,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertWith(getCallerShortcuts())
@@ -4080,7 +4111,7 @@
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
 
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_1, USER_0));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -4099,7 +4130,7 @@
 
         mRunningUsers.put(USER_10, true);
 
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_2, USER_10));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -4126,7 +4157,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4389,7 +4420,7 @@
 
         // Update the package.
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -4524,7 +4555,7 @@
         mRunningUsers.put(USER_10, true);
 
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4552,11 +4583,11 @@
                     .revertToOriginalList()
                     .selectByIds("ms1-alt", "s2")
                     .areAllWithActivity(ACTIVITY2)
-                    ;
+            ;
         });
 
         // First, no changes.
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4579,7 +4610,7 @@
 
         // Disable activity 1
         mEnabledActivityChecker = (activity, userId) -> !ACTIVITY1.equals(activity);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4599,7 +4630,7 @@
         // Re-enable activity 1.
         // Manifest shortcuts will be re-published, but dynamic ones are not.
         mEnabledActivityChecker = (activity, userId) -> true;
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4617,13 +4648,13 @@
                     .revertToOriginalList()
                     .selectByIds("ms1-alt", "s2")
                     .areAllWithActivity(ACTIVITY2)
-                    ;
+            ;
         });
 
         // Disable activity 2
         // Because "ms1-alt" and "s2" are both pinned, they will remain, but disabled.
         mEnabledActivityChecker = (activity, userId) -> !ACTIVITY2.equals(activity);
-                mService.mPackageMonitor.onReceive(getTestContext(),
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4686,7 +4717,7 @@
         setCaller(LAUNCHER_1, USER_0);
         assertForLauncherCallback(mLauncherApps, () -> {
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-                    mService.mPackageMonitor.onReceive(getTestContext(),
+            mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
         }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
                 // Make sure the launcher gets callbacks.
@@ -4708,12 +4739,11 @@
                     .areAllNotDynamic()
                     .areAllDisabled()
                     .areAllPinned()
-                    ;
+            ;
         });
     }
 
     protected void prepareForBackupTest() {
-
         prepareCrossProfileDataSet();
 
         backupAndRestore();
@@ -4749,66 +4779,45 @@
         assertFileExistsWithContent("user-0/shortcut_dump/restore-4.txt");
         assertFileExistsWithContent("user-0/shortcut_dump/restore-5-finish.txt");
 
-        checkBackupAndRestore_success();
+        checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
     public void testBackupAndRestore_backupRestoreTwice() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        dumpsysOnLogcat("Before second backup");
+        checkBackupAndRestore_success(/*firstRestore=*/ true);
+
+        // Run a backup&restore again. Note the shortcuts that weren't restored in the previous
+        // restore are disabled, so they won't be restored again.
+        dumpsysOnLogcat("Before second backup&restore");
 
         backupAndRestore();
 
-        dumpsysOnLogcat("After second backup");
+        dumpsysOnLogcat("After second backup&restore");
 
-        checkBackupAndRestore_success();
-    }
-
-    public void testBackupAndRestore_backupRestoreMultiple() {
-        prepareForBackupTest();
-
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
-        // This also shouldn't affect the result.
-        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
-                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
-        });
-
-        backupAndRestore();
-
-        checkBackupAndRestore_success();
+        checkBackupAndRestore_success(/*firstRestore=*/ false);
     }
 
     public void testBackupAndRestore_restoreToNewVersion() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 5);
 
-        checkBackupAndRestore_success();
+        checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
     public void testBackupAndRestore_restoreToSuperSetSignatures() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         // Change package signatures.
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1, "sigx", CALLING_PACKAGE_1);
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4, LAUNCHER_1, "sigy");
 
-        checkBackupAndRestore_success();
+        checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    protected void checkBackupAndRestore_success() {
+    protected void checkBackupAndRestore_success(boolean firstRestore) {
         // Make sure non-system user is not restored.
         final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
         assertEquals(0, userP0.getAllPackagesForTest().size());
@@ -4818,12 +4827,13 @@
         final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+
+        assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
         assertExistsAndShadow(user0.getAllLaunchersForTest().get(
                 PackageWithUser.of(USER_0, LAUNCHER_1)));
         assertExistsAndShadow(user0.getAllLaunchersForTest().get(
                 PackageWithUser.of(USER_0, LAUNCHER_2)));
 
-        assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
         assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
         assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
 
@@ -4835,14 +4845,16 @@
 
                     .revertToOriginalList()
                     .selectPinned()
-                    .haveIds("s1", "s2");
+                    .haveIds("s1", "s2")
+                    .areAllEnabled();
         });
 
         installPackage(USER_0, LAUNCHER_1);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s1");
+                    .haveIds("s1")
+                    .areAllEnabled();
 
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
                     .isEmpty();
@@ -4862,17 +4874,20 @@
 
                     .revertToOriginalList()
                     .selectPinned()
-                    .haveIds("s1", "s2", "s3");
+                    .haveIds("s1", "s2", "s3")
+                    .areAllEnabled();
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s1");
+                    .haveIds("s1")
+                    .areAllEnabled();
 
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s1", "s2");
+                    .haveIds("s1", "s2")
+                    .areAllEnabled();
 
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
                     .isEmpty();
@@ -4910,14 +4925,28 @@
         runWithCaller(LAUNCHER_2, USER_0, () -> {
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s2");
+                    .haveIds("s2")
+                    .areAllEnabled();
 
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s2", "s3");
+                    .haveIds("s2", "s3")
+                    .areAllEnabled();
 
-            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    .isEmpty();
+            if (firstRestore) {
+                assertWith(
+                        mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                        .haveIds("s2", "s3", "s4")
+                        .areAllDisabled()
+                        .areAllPinned()
+                        .areAllNotDynamic()
+                        .areAllWithDisabledReason(
+                                ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
+            } else {
+                assertWith(
+                        mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                        .isEmpty();
+            }
 
             assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
                     .isEmpty();
@@ -4937,14 +4966,27 @@
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s1");
+                    .haveIds("s1")
+                    .areAllEnabled();
 
             assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
                     .areAllPinned()
-                    .haveIds("s1", "s2");
+                    .haveIds("s1", "s2")
+                    .areAllEnabled();
 
-            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    .isEmpty();
+            if (firstRestore) {
+                assertWith(
+                        mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                        .haveIds("s1", "s2", "s3")
+                        .areAllDisabled()
+                        .areAllPinned()
+                        .areAllNotDynamic()
+                        .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
+            } else {
+                assertWith(
+                        mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                        .isEmpty();
+            }
 
             assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
                     .isEmpty();
@@ -4954,45 +4996,39 @@
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
             assertWith(getCallerVisibleShortcuts())
                     .areAllPinned()
-                    .haveIds("s1", "s2", "s3");
+                    .haveIds("s1", "s2", "s3")
+                    .areAllEnabled();
         });
     }
 
     public void testBackupAndRestore_publisherLowerVersion() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
 
-        checkBackupAndRestore_publisherNotRestored();
+        checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_VERSION_LOWER);
     }
 
     public void testBackupAndRestore_publisherWrongSignature() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
 
-        checkBackupAndRestore_publisherNotRestored();
+        checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH);
     }
 
     public void testBackupAndRestore_publisherNoLongerBackupTarget() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         updatePackageInfo(CALLING_PACKAGE_1,
                 pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
 
-        checkBackupAndRestore_publisherNotRestored();
+        checkBackupAndRestore_publisherNotRestored(
+                ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
     }
 
-    protected void checkBackupAndRestore_publisherNotRestored() {
+    protected void checkBackupAndRestore_publisherNotRestored(
+            int package1DisabledReason) {
         installPackage(USER_0, CALLING_PACKAGE_1);
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertEquals(0, mManager.getDynamicShortcuts().size());
@@ -5014,27 +5050,31 @@
 
         installPackage(USER_0, LAUNCHER_1);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s1", "s2");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .haveIds("s1")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(package1DisabledReason);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .haveIds("s1", "s2")
+                    .areAllPinned()
+                    .areAllEnabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
         });
         installPackage(USER_0, LAUNCHER_2);
         runWithCaller(LAUNCHER_2, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s2", "s3");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .haveIds("s2")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(package1DisabledReason);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .haveIds("s2", "s3")
+                    .areAllPinned()
+                    .areAllEnabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
         });
 
         installPackage(USER_0, CALLING_PACKAGE_3);
@@ -5044,46 +5084,51 @@
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s1", "s2");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .haveIds("s1")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(package1DisabledReason);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .haveIds("s1", "s2")
+                    .areAllPinned()
+                    .areAllEnabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .haveIds("s1", "s2", "s3")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
         });
         runWithCaller(LAUNCHER_2, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s2", "s3");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .haveIds("s2")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(package1DisabledReason);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .haveIds("s2", "s3")
+                    .areAllPinned()
+                    .areAllEnabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .haveIds("s2", "s3", "s4")
+                    .areAllPinned()
+                    .areAllDisabled()
+                    .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
         });
     }
 
     public void testBackupAndRestore_launcherLowerVersion() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
 
-        checkBackupAndRestore_launcherNotRestored();
+        // Note, we restore pinned shortcuts even if the launcher is of a lower version.
+        checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
     public void testBackupAndRestore_launcherWrongSignature() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
 
         checkBackupAndRestore_launcherNotRestored();
@@ -5092,9 +5137,6 @@
     public void testBackupAndRestore_launcherNoLongerBackupTarget() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         updatePackageInfo(LAUNCHER_1,
                 pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
 
@@ -5185,18 +5227,12 @@
             assertShortcutIds(assertAllPinned(
                     mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
                     "s2", "s3");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
         });
     }
 
     public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
         prepareForBackupTest();
 
-        // Note doing a backup & restore again here shouldn't affect the result.
-        backupAndRestore();
-
         updatePackageInfo(CALLING_PACKAGE_1,
                 pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
 
@@ -5235,15 +5271,15 @@
         });
         installPackage(USER_0, LAUNCHER_2);
         runWithCaller(LAUNCHER_2, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s2", "s3");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2")
+                    .areAllDisabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2", "s3");
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
         });
 
         // Because launcher 1 wasn't restored, "s1" is no longer pinned.
@@ -5272,15 +5308,21 @@
                     /* empty */);
         });
         runWithCaller(LAUNCHER_2, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
-                    /* empty */);
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s2", "s3");
-            assertShortcutIds(assertAllPinned(
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2")
+                    .areAllDisabled();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2", "s3");
+            assertWith(
                     mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty */);
+                    .haveIds("s2", "s3", "s4")
+                    .areAllDisabled()
+                    .areAllPinned()
+                    .areAllNotDynamic()
+                    .areAllWithDisabledReason(
+                            ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED);
         });
     }
 
@@ -5305,12 +5347,12 @@
         final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+        assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
         assertExistsAndShadow(user0.getAllLaunchersForTest().get(
                 PackageWithUser.of(USER_0, LAUNCHER_1)));
         assertExistsAndShadow(user0.getAllLaunchersForTest().get(
                 PackageWithUser.of(USER_0, LAUNCHER_2)));
 
-        assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
         assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
         assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
 
@@ -5421,7 +5463,7 @@
                     .revertToOriginalList()
                     .selectByIds("s1", "s2")
                     .areAllNotDynamic()
-                    ;
+            ;
         });
     }
 
@@ -5555,6 +5597,287 @@
         });
     }
 
+
+    /**
+     * Restored to a lower version with no manifest shortcuts. All shortcuts are now invisible,
+     * and all calls from the publisher should ignore them.
+     */
+    public void testBackupAndRestore_disabledShortcutsAreIgnored() {
+        // Publish two manifest shortcuts.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_5_altalt);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(mServiceContext,
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcutWithShortLabel("s1", "original-title"),
+                    makeShortcut("s2"), makeShortcut("s3"))));
+        });
+
+        // Pin from launcher 1.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+                    list("ms1", "ms2", "ms3", "ms4", "s1", "s2"), HANDLE_USER_0);
+        });
+
+        backupAndRestore();
+
+        // Lower the version and remove the manifest shortcuts.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_0);
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
+
+        // When re-installing the app, the manifest shortcut should be re-published.
+        mService.mPackageMonitor.onReceive(mServiceContext,
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+        mService.mPackageMonitor.onReceive(mServiceContext,
+                genPackageAddIntent(LAUNCHER_1, USER_0));
+
+        // No shortcuts should be visible to the publisher.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerVisibleShortcuts())
+                    .isEmpty();
+        });
+
+        final Runnable checkAllDisabledForLauncher = () -> {
+            runWithCaller(LAUNCHER_1, USER_0, () -> {
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .areAllPinned()
+                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
+                        .areAllDisabled()
+                        .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+
+                        .forShortcutWithId("s1", si -> {
+                            assertEquals("original-title", si.getShortLabel());
+                        })
+                        .forShortcutWithId("ms1", si -> {
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title1 + "/en"
+                                    , si.getShortLabel());
+                        })
+                        .forShortcutWithId("ms2", si -> {
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title2 + "/en"
+                                    , si.getShortLabel());
+                        })
+                        .forShortcutWithId("ms3", si -> {
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title1 + "/en"
+                                    , si.getShortLabel());
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title2 + "/en"
+                                    , si.getLongLabel());
+                        })
+                        .forShortcutWithId("ms4", si -> {
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title2 + "/en"
+                                    , si.getShortLabel());
+                            assertEquals("string-com.android.test.1-user:0-res:"
+                                            + R.string.shortcut_title2 + "/en"
+                                    , si.getLongLabel());
+                        });
+            });
+        };
+
+        checkAllDisabledForLauncher.run();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+
+            makeCallerForeground(); // CALLING_PACKAGE_1 is now in the foreground.
+
+            // All changing API calls should be ignored.
+
+            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
+            checkAllDisabledForLauncher.run();
+
+            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
+            checkAllDisabledForLauncher.run();
+
+            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
+            checkAllDisabledForLauncher.run();
+
+            getManager().removeAllDynamicShortcuts();
+            getManager().removeDynamicShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
+            checkAllDisabledForLauncher.run();
+
+            getManager().updateShortcuts(list(makeShortcutWithShortLabel("s1", "new-title")));
+            checkAllDisabledForLauncher.run();
+
+
+            // Add a shortcut -- even though ms1 was immutable, it will succeed.
+            assertTrue(getManager().addDynamicShortcuts(list(
+                    makeShortcutWithShortLabel("ms1", "original-title"))));
+
+            runWithCaller(LAUNCHER_1, USER_0, () -> {
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
+
+                        .selectByIds("ms1")
+                        .areAllEnabled()
+                        .areAllDynamic()
+                        .areAllPinned()
+                        .forAllShortcuts(si -> {
+                            assertEquals("original-title", si.getShortLabel());
+                        })
+
+                        // The rest still exist and disabled.
+                        .revertToOriginalList()
+                        .selectByIds("ms2", "ms3", "ms4", "s1", "s2")
+                        .areAllDisabled()
+                        .areAllPinned()
+                ;
+            });
+
+            assertTrue(getManager().setDynamicShortcuts(list(
+                    makeShortcutWithShortLabel("ms2", "new-title-2"))));
+
+            runWithCaller(LAUNCHER_1, USER_0, () -> {
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
+
+                        .selectByIds("ms1")
+                        .areAllEnabled()
+                        .areAllNotDynamic() // ms1 was not in the list, so no longer dynamic.
+                        .areAllPinned()
+                        .areAllMutable()
+                        .forAllShortcuts(si -> {
+                            assertEquals("original-title", si.getShortLabel());
+                        })
+
+                        .revertToOriginalList()
+                        .selectByIds("ms2")
+                        .areAllEnabled()
+                        .areAllDynamic()
+                        .areAllPinned()
+                        .areAllMutable()
+                        .forAllShortcuts(si -> {
+                            assertEquals("new-title-2", si.getShortLabel());
+                        })
+
+                        // The rest still exist and disabled.
+                        .revertToOriginalList()
+                        .selectByIds("ms3", "ms4", "s1", "s2")
+                        .areAllDisabled()
+                        .areAllPinned()
+                ;
+            });
+
+            // Prepare for requestPinShortcut().
+            setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+            mPinConfirmActivityFetcher = (packageName, userId) ->
+                    new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
+
+            mManager.requestPinShortcut(
+                    makeShortcutWithShortLabel("ms3", "new-title-3"),
+                    /*PendingIntent=*/ null);
+
+            // Note this was pinned, so it'll be accepted right away.
+            runWithCaller(LAUNCHER_1, USER_0, () -> {
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .selectByIds("ms3")
+                        .areAllEnabled()
+                        .areAllNotDynamic()
+                        .areAllPinned()
+                        .areAllMutable()
+                        .forAllShortcuts(si -> {
+                            assertEquals("new-title-3", si.getShortLabel());
+                            // The new one replaces the old manifest shortcut, so the long label
+                            // should be gone now.
+                            assertNull(si.getLongLabel());
+                        });
+            });
+
+            // Now, change the launcher to launcher2, and request pin again.
+            setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
+
+            reset(mServiceContext);
+
+            assertTrue(mManager.isRequestPinShortcutSupported());
+            mManager.requestPinShortcut(
+                    makeShortcutWithShortLabel("ms4", "new-title-4"),
+                    /*PendingIntent=*/ null);
+
+            // Initially there should be no pinned shortcuts for L2.
+            runWithCaller(LAUNCHER_2, USER_0, () -> {
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .selectPinned()
+                        .isEmpty();
+
+                final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+                verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+                assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT,
+                        intent.getValue().getAction());
+                assertEquals(LAUNCHER_2, intent.getValue().getComponent().getPackageName());
+
+                // Check the request object.
+                final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+                assertNotNull(request);
+                assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());
+
+                assertWith(request.getShortcutInfo())
+                        .haveIds("ms4")
+                        .areAllOrphan()
+                        .forAllShortcuts(si -> {
+                            assertEquals("new-title-4", si.getShortLabel());
+                            // The new one replaces the old manifest shortcut, so the long label
+                            // should be gone now.
+                            assertNull(si.getLongLabel());
+                        });
+                assertTrue(request.accept());
+
+                assertWith(getShortcutAsLauncher(USER_0))
+                        .selectPinned()
+                        .haveIds("ms4")
+                        .areAllEnabled();
+            });
+        });
+    }
+
+    /**
+     * Test for restoring the pre-P backup format.
+     */
+    public void testBackupAndRestore_api27format() throws Exception {
+        final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes();
+
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222");
+        addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "11111");
+
+        runWithSystemUid(() -> mService.applyRestore(payload, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerShortcuts())
+                    .areAllPinned()
+                    .haveIds("s1")
+                    .areAllEnabled();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertWith(getShortcutAsLauncher(USER_0))
+                    .areAllPinned()
+                    .haveIds("s1")
+                    .areAllEnabled();
+        });
+        // Make sure getBackupSourceVersionCode and isBackupSourceBackupAllowed
+        // are correct. We didn't have them in the old format.
+        assertEquals(8, mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0)
+                .getPackageInfo().getBackupSourceVersionCode());
+        assertTrue(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0)
+                .getPackageInfo().isBackupSourceBackupAllowed());
+
+        assertEquals(9, mService.getLauncherShortcutForTest(LAUNCHER_1, USER_0)
+                .getPackageInfo().getBackupSourceVersionCode());
+        assertTrue(mService.getLauncherShortcutForTest(LAUNCHER_1, USER_0)
+                .getPackageInfo().isBackupSourceBackupAllowed());
+
+    }
+
     public void testSaveAndLoad_crossProfile() {
         prepareCrossProfileDataSet();
 
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 3220ea9..fcdadac 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -1131,7 +1131,8 @@
         assertEquals(0, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED
+                | ShortcutInfo.FLAG_SHADOW , si.getFlags());
         assertNull(si.getBitmapPath()); // No icon.
         assertEquals(0, si.getIconResourceId());
 
@@ -1198,7 +1199,8 @@
         assertEquals(0, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED
+                | ShortcutInfo.FLAG_SHADOW , si.getFlags());
         assertNull(si.getBitmapPath()); // No icon.
         assertEquals(0, si.getIconResourceId());
         assertEquals(null, si.getIconResName());
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 536a504..71bcae7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -16,19 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
-import android.content.res.Configuration;
-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;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -36,6 +24,15 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.After;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
 /**
  * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
  *
@@ -52,12 +49,8 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        final Configuration overrideConfig = new Configuration();
-        overrideConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
-        mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
-                mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
-        mPinnedStack.onOverrideConfigurationChanged(overrideConfig);
-
+        mPinnedStack = createStackControllerOnStackOnDisplay(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
         assertFalse(mPinnedStack.isVisible());
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 4e810f2..56a3fb0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -36,8 +36,6 @@
 import android.view.IWindow;
 import android.view.WindowManager;
 
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -70,7 +68,7 @@
     // make sure we don't collide with any existing display. If we run into no other display, the
     // added display should be treated as default. This cannot be the default display
     private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
-    private static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
+    static int sNextStackId = 1000;
 
     private static boolean sOneTimeSetupDone = false;
     DisplayContent mDisplayContent;
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 926a606..a4349f4 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -22,6 +22,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyList;
 import static org.mockito.Matchers.anyString;
@@ -59,9 +60,7 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
 import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
 import org.mockito.hamcrest.MockitoHamcrest;
 
 import java.io.BufferedReader;
@@ -898,11 +897,14 @@
 
         public ShortcutListAsserter areAllEnabled() {
             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
+            areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
             return this;
         }
 
         public ShortcutListAsserter areAllDisabled() {
             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
+            forAllShortcuts(s -> assertNotEquals("id=" + s.getId(),
+                    ShortcutInfo.DISABLED_REASON_NOT_DISABLED, s.getDisabledReason()));
             return this;
         }
 
@@ -930,6 +932,16 @@
             return this;
         }
 
+        public ShortcutListAsserter areAllVisibleToPublisher() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isVisibleToPublisher()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotVisibleToPublisher() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isVisibleToPublisher()));
+            return this;
+        }
+
         public ShortcutListAsserter areAllWithKeyFieldsOnly() {
             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
             return this;
@@ -960,6 +972,17 @@
             return this;
         }
 
+        public ShortcutListAsserter areAllWithDisabledReason(int disabledReason) {
+            forAllShortcuts(s -> assertEquals("id=" + s.getId(),
+                    disabledReason, s.getDisabledReason()));
+            if (disabledReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+                areAllVisibleToPublisher();
+            } else {
+                areAllNotVisibleToPublisher();
+            }
+            return this;
+        }
+
         public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
             boolean found = false;
             for (int i = 0; i < mList.size(); i++) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index acc27be..d359b70 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -132,7 +132,9 @@
         mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
 
         // initial scan
-        mCardsParser.scan();
+        if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) {
+            Slog.e(TAG, "Error scanning ASLA cards file.");
+        }
     }
 
     public void systemReady() {
@@ -314,7 +316,7 @@
             return null;
         }
 
-        if (!mDevicesParser.scan()) {
+        if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) {
             Slog.e(TAG, "Error parsing ALSA devices file.");
             return null;
         }
@@ -530,6 +532,9 @@
     //
     // called by UsbService.dump
     public void dump(IndentingPrintWriter pw) {
+        pw.println("Parsers Scan Status:");
+        pw.println("  Cards Parser: " + mCardsParser.getScanStatus());
+        pw.println("  Devices Parser: " + mDevicesParser.getScanStatus());
         pw.println("USB Audio Devices:");
         for (UsbDevice device : mAudioDevices.keySet()) {
             pw.println("  " + device.getDeviceName() + ": " + mAudioDevices.get(device));
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index c657a1b..095fdc6 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -376,6 +376,8 @@
                 }
             }
         }
+
+        mUsbAlsaManager.dump(pw);
     }
 
     private native void monitorUsbHostBus();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 689ce95..72685fa 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1573,6 +1573,13 @@
     public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
             "show_ims_registration_status_bool";
 
+    /**
+     * The flag to disable the popup dialog which warns the user of data charges.
+     * @hide
+     */
+    public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL =
+            "disable_charge_indication_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1840,6 +1847,7 @@
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+        sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 764b7b2..9a9877a 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -77,8 +77,9 @@
      * Integer extra that Android will attach to the intent supplied via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
      * Indicates the result code of the download. One of
-     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED}, or
-     * {@link #RESULT_IO_ERROR}.
+     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED},
+     * {@link #RESULT_IO_ERROR}, {@link #RESULT_DOWNLOAD_FAILURE}, {@link #RESULT_OUT_OF_STORAGE},
+     * {@link #RESULT_SERVICE_ID_NOT_DEFINED}, or {@link #RESULT_FILE_ROOT_UNREACHABLE}.
      *
      * This extra may also be used by the middleware when it is sending intents to the app.
      */
@@ -142,11 +143,41 @@
 
     /**
      * Indicates that the download will not be completed due to an I/O error incurred while
-     * writing to temp files. This commonly indicates that the device is out of storage space,
-     * but may indicate other conditions as well (such as an SD card being removed).
+     * writing to temp files.
+     *
+     * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+     * the download again.
      */
     public static final int RESULT_IO_ERROR = 4;
-    // TODO - more results!
+
+    /**
+     * Indicates that the Service ID specified in the {@link DownloadRequest} is incorrect due to
+     * the Id being incorrect, stale, expired, or similar.
+     */
+    public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5;
+
+    /**
+     * Indicates that there was an error while processing downloaded files, such as a file repair or
+     * file decoding error and is not due to a file I/O error.
+     *
+     * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+     * the download again.
+     */
+    public static final int RESULT_DOWNLOAD_FAILURE = 6;
+
+    /**
+     * Indicates that the file system is full and the {@link DownloadRequest} can not complete.
+     * Either space must be made on the current file system or the temp file root location must be
+     * changed to a location that is not full to download the temp files.
+     */
+    public static final int RESULT_OUT_OF_STORAGE = 7;
+
+    /**
+     * Indicates that the file root that was set is currently unreachable. This can happen if the
+     * temp files are set to be stored on external storage and the SD card was removed, for example.
+     * The temp file root should be changed before sending another DownloadRequest.
+     */
+    public static final int RESULT_FILE_ROOT_UNREACHABLE = 8;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 5a57f32..f0d60b6 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Intent;
 import android.net.Uri;
@@ -26,7 +27,6 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -71,6 +71,19 @@
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+
+        /**
+         * Builds a new DownloadRequest.
+         * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
+         *     never be null.
+         */
+        public Builder(@NonNull Uri sourceUri) {
+            if (sourceUri == null) {
+                throw new IllegalArgumentException("Source URI must be non-null.");
+            }
+            source = sourceUri;
+        }
+
         /**
          * Sets the service from which the download request to be built will download from.
          * @param serviceInfo
@@ -92,15 +105,6 @@
         }
 
         /**
-         * Sets the source URI for the download request to be built.
-         * @param source
-         */
-        public Builder setSource(Uri source) {
-            this.source = source;
-            return this;
-        }
-
-        /**
          * Set the subscription ID on which the file(s) should be downloaded.
          * @param subscriptionId
          */
@@ -316,9 +320,11 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI, destination URI, and the app intent
+            // Hash the source URI and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
-            digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+            if (serializedResultIntentForApp != null) {
+                digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+            }
         }
         // Add updates for future versions here
         return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index fe27537..9af1eb9 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -287,7 +287,7 @@
             return;
         }
 
-        List<Uri> tempFiles = intent.getParcelableExtra(VendorUtils.EXTRA_TEMP_LIST);
+        List<Uri> tempFiles = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_LIST);
         if (tempFiles == null) {
             return;
         }
@@ -309,7 +309,7 @@
             return;
         }
         int fdCount = intent.getIntExtra(VendorUtils.EXTRA_FD_COUNT, 0);
-        List<Uri> pausedList = intent.getParcelableExtra(VendorUtils.EXTRA_PAUSED_LIST);
+        List<Uri> pausedList = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_PAUSED_LIST);
 
         if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
             Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -492,9 +492,14 @@
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException("Package manager couldn't find " + context.getPackageName());
         }
+        if (appInfo.metaData == null) {
+            throw new RuntimeException("App must declare the file provider authority as metadata " +
+                    "in the manifest.");
+        }
         String authority = appInfo.metaData.getString(MBMS_FILE_PROVIDER_META_DATA_KEY);
         if (authority == null) {
-            throw new RuntimeException("Must declare the file provider authority as meta data");
+            throw new RuntimeException("App must declare the file provider authority as metadata " +
+                    "in the manifest.");
         }
         return authority;
     }
diff --git a/tests/CantSaveState1/Android.mk b/tests/CantSaveState1/Android.mk
new file mode 100644
index 0000000..6e9db6e
--- /dev/null
+++ b/tests/CantSaveState1/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CantSaveState1
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CantSaveState1/AndroidManifest.xml b/tests/CantSaveState1/AndroidManifest.xml
new file mode 100644
index 0000000..fadcaeb
--- /dev/null
+++ b/tests/CantSaveState1/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.cantsavestate1">
+    <application android:label="Can't Save 1" android:cantSaveState="true">
+        <activity android:name="CantSave1Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/CantSaveState1/res/layout/cant_save_1_activity.xml b/tests/CantSaveState1/res/layout/cant_save_1_activity.xml
new file mode 100644
index 0000000..c5bf657
--- /dev/null
+++ b/tests/CantSaveState1/res/layout/cant_save_1_activity.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="This app #1 can't save its state"
+    />
+
+</LinearLayout>
diff --git a/cmds/statsd/src/parse_util.h b/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
index 8b82e7b..8879ed0 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
@@ -13,24 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
 
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package com.android.test.cantsavestate1;
 
-#include <log/logprint.h>
+import android.app.Activity;
+import android.os.Bundle;
 
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-
-#endif  // PARSE_UTIL_H
+public class CantSave1Activity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cant_save_1_activity);
+    }
+}
diff --git a/tests/CantSaveState2/Android.mk b/tests/CantSaveState2/Android.mk
new file mode 100644
index 0000000..add9214
--- /dev/null
+++ b/tests/CantSaveState2/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CantSaveState2
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CantSaveState2/AndroidManifest.xml b/tests/CantSaveState2/AndroidManifest.xml
new file mode 100644
index 0000000..8f4f01d
--- /dev/null
+++ b/tests/CantSaveState2/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.cantsavestate2">
+    <application android:label="Can't Save 2" android:cantSaveState="true">
+        <activity android:name="CantSave2Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/CantSaveState2/res/layout/cant_save_2_activity.xml b/tests/CantSaveState2/res/layout/cant_save_2_activity.xml
new file mode 100644
index 0000000..c5b8e3d
--- /dev/null
+++ b/tests/CantSaveState2/res/layout/cant_save_2_activity.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="This app #2 can't save its state"
+    />
+
+</LinearLayout>
diff --git a/cmds/statsd/src/parse_util.h b/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
index 8b82e7b..3ce63c7 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
@@ -13,24 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
 
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package com.android.test.cantsavestate2;
 
-#include <log/logprint.h>
+import android.app.Activity;
+import android.os.Bundle;
 
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-
-#endif  // PARSE_UTIL_H
+public class CantSave2Activity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.cant_save_2_activity);
+    }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 8210403..163250d 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -296,6 +296,7 @@
                     Notification n = new Notification.Builder(NotificationTestList.this, "min")
                             .setSmallIcon(R.drawable.icon2)
                             .setContentTitle("Min priority")
+                            .setTicker("Min priority")
                             .build();
                     mNM.notify("min", 7000, n);
                 }
@@ -306,6 +307,7 @@
                     Notification n = new Notification.Builder(NotificationTestList.this, "low")
                             .setSmallIcon(R.drawable.icon2)
                             .setContentTitle("Low priority")
+                            .setTicker("Low priority")
                             .build();
                     mNM.notify("low", 7002, n);
                 }
@@ -326,6 +328,7 @@
                     Notification n = new Notification.Builder(NotificationTestList.this, "high")
                             .setSmallIcon(R.drawable.icon2)
                             .setContentTitle("High priority")
+                            .setTicker("High priority")
                             .build();
                     mNM.notify("high", 7006, n);
                 }
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 9f31d27..ccb0f3b 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -31,19 +31,21 @@
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.system.Os;
-import android.test.AndroidTestCase;
+
 import com.android.server.IpSecService;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 /** Unit tests for {@link IpSecManager}. */
 @SmallTest
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 public class IpSecManagerTest {
 
     private static final int TEST_UDP_ENCAP_PORT = 34567;
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
similarity index 80%
rename from core/tests/coretests/src/android/net/LinkPropertiesTest.java
rename to tests/net/java/android/net/LinkPropertiesTest.java
index 1cb0ecd..52da79a 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -16,19 +16,22 @@
 
 package android.net;
 
+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 android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.RouteInfo;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.system.OsConstants;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.ArraySet;
 
-import junit.framework.TestCase;
-
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,8 +40,12 @@
 import java.util.List;
 import java.util.Set;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class LinkPropertiesTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LinkPropertiesTest {
     private static InetAddress ADDRV4 = NetworkUtils.numericToInetAddress("75.208.6.1");
     private static InetAddress ADDRV6 = NetworkUtils.numericToInetAddress(
             "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
@@ -92,7 +99,7 @@
         assertEquals(source.hashCode(), target.hashCode());
     }
 
-    @SmallTest
+    @Test
     public void testEqualsNull() {
         LinkProperties source = new LinkProperties();
         LinkProperties target = new LinkProperties();
@@ -101,155 +108,141 @@
         assertLinkPropertiesEqual(source, target);
     }
 
-    @SmallTest
-    public void testEqualsSameOrder() {
-        try {
-            LinkProperties source = new LinkProperties();
-            source.setInterfaceName(NAME);
-            // set 2 link addresses
-            source.addLinkAddress(LINKADDRV4);
-            source.addLinkAddress(LINKADDRV6);
-            // set 2 dnses
-            source.addDnsServer(DNS1);
-            source.addDnsServer(DNS2);
-            // set 2 gateways
-            source.addRoute(new RouteInfo(GATEWAY1));
-            source.addRoute(new RouteInfo(GATEWAY2));
-            source.setMtu(MTU);
+    @Test
+    public void testEqualsSameOrder() throws Exception {
+        LinkProperties source = new LinkProperties();
+        source.setInterfaceName(NAME);
+        // set 2 link addresses
+        source.addLinkAddress(LINKADDRV4);
+        source.addLinkAddress(LINKADDRV6);
+        // set 2 dnses
+        source.addDnsServer(DNS1);
+        source.addDnsServer(DNS2);
+        // set 2 gateways
+        source.addRoute(new RouteInfo(GATEWAY1));
+        source.addRoute(new RouteInfo(GATEWAY2));
+        source.setMtu(MTU);
 
-            LinkProperties target = new LinkProperties();
+        LinkProperties target = new LinkProperties();
 
-            // All fields are same
-            target.setInterfaceName(NAME);
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            target.addDnsServer(DNS1);
-            target.addDnsServer(DNS2);
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.setMtu(MTU);
+        // All fields are same
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(DNS1);
+        target.addDnsServer(DNS2);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
 
-            assertLinkPropertiesEqual(source, target);
+        assertLinkPropertiesEqual(source, target);
 
-            target.clear();
-            // change Interface Name
-            target.setInterfaceName("qmi1");
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            target.addDnsServer(DNS1);
-            target.addDnsServer(DNS2);
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.setMtu(MTU);
-            assertFalse(source.equals(target));
+        target.clear();
+        // change Interface Name
+        target.setInterfaceName("qmi1");
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(DNS1);
+        target.addDnsServer(DNS2);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
+        assertFalse(source.equals(target));
 
-            target.clear();
-            target.setInterfaceName(NAME);
-            // change link addresses
-            target.addLinkAddress(new LinkAddress(
-                    NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
-            target.addLinkAddress(LINKADDRV6);
-            target.addDnsServer(DNS1);
-            target.addDnsServer(DNS2);
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.setMtu(MTU);
-            assertFalse(source.equals(target));
+        target.clear();
+        target.setInterfaceName(NAME);
+        // change link addresses
+        target.addLinkAddress(new LinkAddress(
+                NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(DNS1);
+        target.addDnsServer(DNS2);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
+        assertFalse(source.equals(target));
 
-            target.clear();
-            target.setInterfaceName(NAME);
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            // change dnses
-            target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
-            target.addDnsServer(DNS2);
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.setMtu(MTU);
-            assertFalse(source.equals(target));
+        target.clear();
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        // change dnses
+        target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+        target.addDnsServer(DNS2);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
+        assertFalse(source.equals(target));
 
-            target.clear();
-            target.setInterfaceName(NAME);
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            target.addDnsServer(DNS1);
-            target.addDnsServer(DNS2);
-            // change gateway
-            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.setMtu(MTU);
-            assertFalse(source.equals(target));
+        target.clear();
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(DNS1);
+        target.addDnsServer(DNS2);
+        // change gateway
+        target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.setMtu(MTU);
+        assertFalse(source.equals(target));
 
-            target.clear();
-            target.setInterfaceName(NAME);
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            target.addDnsServer(DNS1);
-            target.addDnsServer(DNS2);
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.addRoute(new RouteInfo(GATEWAY2));
-            // change mtu
-            target.setMtu(1440);
-            assertFalse(source.equals(target));
-
-        } catch (Exception e) {
-            throw new RuntimeException(e.toString());
-            //fail();
-        }
+        target.clear();
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addDnsServer(DNS1);
+        target.addDnsServer(DNS2);
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(GATEWAY2));
+        // change mtu
+        target.setMtu(1440);
+        assertFalse(source.equals(target));
     }
 
-    @SmallTest
-    public void testEqualsDifferentOrder() {
-        try {
-            LinkProperties source = new LinkProperties();
-            source.setInterfaceName(NAME);
-            // set 2 link addresses
-            source.addLinkAddress(LINKADDRV4);
-            source.addLinkAddress(LINKADDRV6);
-            // set 2 dnses
-            source.addDnsServer(DNS1);
-            source.addDnsServer(DNS2);
-            // set 2 gateways
-            source.addRoute(new RouteInfo(GATEWAY1));
-            source.addRoute(new RouteInfo(GATEWAY2));
-            source.setMtu(MTU);
+    @Test
+    public void testEqualsDifferentOrder() throws Exception {
+        LinkProperties source = new LinkProperties();
+        source.setInterfaceName(NAME);
+        // set 2 link addresses
+        source.addLinkAddress(LINKADDRV4);
+        source.addLinkAddress(LINKADDRV6);
+        // set 2 dnses
+        source.addDnsServer(DNS1);
+        source.addDnsServer(DNS2);
+        // set 2 gateways
+        source.addRoute(new RouteInfo(GATEWAY1));
+        source.addRoute(new RouteInfo(GATEWAY2));
+        source.setMtu(MTU);
 
-            LinkProperties target = new LinkProperties();
-            // Exchange order
-            target.setInterfaceName(NAME);
-            target.addLinkAddress(LINKADDRV6);
-            target.addLinkAddress(LINKADDRV4);
-            target.addDnsServer(DNS2);
-            target.addDnsServer(DNS1);
-            target.addRoute(new RouteInfo(GATEWAY2));
-            target.addRoute(new RouteInfo(GATEWAY1));
-            target.setMtu(MTU);
+        LinkProperties target = new LinkProperties();
+        // Exchange order
+        target.setInterfaceName(NAME);
+        target.addLinkAddress(LINKADDRV6);
+        target.addLinkAddress(LINKADDRV4);
+        target.addDnsServer(DNS2);
+        target.addDnsServer(DNS1);
+        target.addRoute(new RouteInfo(GATEWAY2));
+        target.addRoute(new RouteInfo(GATEWAY1));
+        target.setMtu(MTU);
 
-            assertLinkPropertiesEqual(source, target);
-        } catch (Exception e) {
-            fail();
-        }
+        assertLinkPropertiesEqual(source, target);
     }
 
-    @SmallTest
-    public void testEqualsDuplicated() {
-        try {
-            LinkProperties source = new LinkProperties();
-            // set 3 link addresses, eg, [A, A, B]
-            source.addLinkAddress(LINKADDRV4);
-            source.addLinkAddress(LINKADDRV4);
-            source.addLinkAddress(LINKADDRV6);
+    @Test
+    public void testEqualsDuplicated() throws Exception {
+        LinkProperties source = new LinkProperties();
+        // set 3 link addresses, eg, [A, A, B]
+        source.addLinkAddress(LINKADDRV4);
+        source.addLinkAddress(LINKADDRV4);
+        source.addLinkAddress(LINKADDRV6);
 
-            LinkProperties target = new LinkProperties();
-            // set 3 link addresses, eg, [A, B, B]
-            target.addLinkAddress(LINKADDRV4);
-            target.addLinkAddress(LINKADDRV6);
-            target.addLinkAddress(LINKADDRV6);
+        LinkProperties target = new LinkProperties();
+        // set 3 link addresses, eg, [A, B, B]
+        target.addLinkAddress(LINKADDRV4);
+        target.addLinkAddress(LINKADDRV6);
+        target.addLinkAddress(LINKADDRV6);
 
-            assertLinkPropertiesEqual(source, target);
-        } catch (Exception e) {
-            fail();
-        }
+        assertLinkPropertiesEqual(source, target);
     }
 
     private void assertAllRoutesHaveInterface(String iface, LinkProperties lp) {
@@ -258,7 +251,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testRouteInterfaces() {
         LinkAddress prefix = new LinkAddress(
             NetworkUtils.numericToInetAddress("2001:db8::"), 32);
@@ -317,7 +310,7 @@
         assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
     }
 
-    @SmallTest
+    @Test
     public void testStackedInterfaces() {
         LinkProperties rmnet0 = new LinkProperties();
         rmnet0.setInterfaceName("rmnet0");
@@ -373,7 +366,7 @@
         return lp.getLinkAddresses().iterator().next();
     }
 
-    @SmallTest
+    @Test
     public void testAddressMethods() {
         LinkProperties lp = new LinkProperties();
 
@@ -460,7 +453,7 @@
         assertEquals(0, lp.getLinkAddresses().size());
     }
 
-    @SmallTest
+    @Test
     public void testSetLinkAddresses() {
         LinkProperties lp = new LinkProperties();
         lp.addLinkAddress(LINKADDRV4);
@@ -475,7 +468,7 @@
         assertTrue(lp.equals(lp));
     }
 
-    @SmallTest
+    @Test
     public void testIsProvisioned() {
         LinkProperties lp4 = new LinkProperties();
         assertFalse("v4only:empty", lp4.isProvisioned());
@@ -529,7 +522,7 @@
         assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned());
     }
 
-    @SmallTest
+    @Test
     public void testCompareProvisioning() {
         LinkProperties v4lp = new LinkProperties();
         v4lp.addLinkAddress(LINKADDRV4);
@@ -589,8 +582,7 @@
                 LinkProperties.compareProvisioning(v6lp, v6lp2));
     }
 
-    @SmallTest
-    @Suppress  // Failing.
+    @Test
     public void testIsReachable() {
         final LinkProperties v4lp = new LinkProperties();
         assertFalse(v4lp.isReachable(DNS1));
@@ -687,7 +679,7 @@
         assertTrue(v6lp.isReachable(DNS1));
     }
 
-    @SmallTest
+    @Test
     public void testLinkPropertiesEnsureDirectlyConnectedRoutes() {
         // IPv4 case: no route added initially
         LinkProperties rmnet0 = new LinkProperties();
@@ -750,25 +742,25 @@
 
     }
 
-    @SmallTest
+    @Test
     public void testCompareResult() {
         // Either adding or removing items
-        testCompareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
+        compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
                 Arrays.asList(2, 3, 4), new ArrayList<>());
-        testCompareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4),
+        compareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4),
                 new ArrayList<>(), Arrays.asList(3, 4));
 
 
         // adding and removing items at the same time
-        testCompareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5),
+        compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5),
                 Arrays.asList(1), Arrays.asList(5));
-        testCompareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6),
+        compareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6),
                 Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
 
         // null cases
-        testCompareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>());
-        testCompareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3));
-        testCompareResult(null, null, new ArrayList<>(), new ArrayList<>());
+        compareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>());
+        compareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3));
+        compareResult(null, null, new ArrayList<>(), new ArrayList<>());
     }
 
     private void assertEqualRoutes(Collection<RouteInfo> expected, Collection<RouteInfo> actual) {
@@ -780,7 +772,7 @@
         assertEquals(expectedSet, actualSet);
     }
 
-    private <T> void testCompareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved,
+    private <T> void compareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved,
             List<T> expectAdded) {
         CompareResult<T> result = new CompareResult<>(oldItems, newItems);
         assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index eb85eb4..25289ba 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -30,23 +30,30 @@
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 
 import com.google.android.collect.Sets;
 
-import junit.framework.TestCase;
-
 import java.util.HashSet;
 
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
 @SmallTest
-public class NetworkStatsTest extends TestCase {
+public class NetworkStatsTest {
 
     private static final String TEST_IFACE = "test0";
     private static final String TEST_IFACE2 = "test2";
     private static final int TEST_UID = 1001;
     private static final long TEST_START = 1194220800000L;
 
+    @Test
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 5)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
@@ -74,6 +81,7 @@
                 ROAMING_NO));
     }
 
+    @Test
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
@@ -116,6 +124,7 @@
         }
     }
 
+    @Test
     public void testAddEntryGrow() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 4);
 
@@ -168,6 +177,7 @@
                 ROAMING_YES, 7L, 70L, 5L, 50L, 11);
     }
 
+    @Test
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
@@ -190,6 +200,7 @@
                 256L, 2L, 256L, 2L, 6);
     }
 
+    @Test
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
@@ -208,6 +219,7 @@
                 0L, 0L, 0L, 0);
     }
 
+    @Test
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
@@ -226,6 +238,7 @@
                 1L, 4L, 1L, 8);
     }
 
+    @Test
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
@@ -247,6 +260,7 @@
                 1024L, 8L, 1024L, 8L, 20);
     }
 
+    @Test
     public void testSubtractMissingRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
                 .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
@@ -264,6 +278,7 @@
         assertEquals(4L, result.getTotalBytes());
     }
 
+    @Test
     public void testTotalBytes() throws Exception {
         final NetworkStats iface = new NetworkStats(TEST_START, 2)
                 .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
@@ -304,6 +319,7 @@
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
 
+    @Test
     public void testGroupedByIfaceEmpty() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3);
         final NetworkStats grouped = uidStats.groupedByIface();
@@ -312,6 +328,7 @@
         assertEquals(0, grouped.size());
     }
 
+    @Test
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
                 .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, 0L,
@@ -329,6 +346,7 @@
                 384L, 24L, 0L, 6L, 0L);
     }
 
+    @Test
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
@@ -357,6 +375,7 @@
                 1024L, 64L, 0L, 0L, 0L);
     }
 
+    @Test
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
@@ -387,6 +406,7 @@
                 32L, 0L, 0L, 0L, 0L);
     }
 
+    @Test
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
@@ -415,6 +435,7 @@
         assertValues(stats.getTotal(null, ifaces), 1024L, 64L, 0L, 0L, 0L);
     }
 
+    @Test
     public void testWithoutUid() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 3)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
@@ -433,6 +454,7 @@
                 8L, 0L, 0L, 0L);
     }
 
+    @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
                 .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
@@ -449,6 +471,7 @@
         assertEquals(128L + 512L, clone.getTotalBytes());
     }
 
+    @Test
     public void testAddWhenEmpty() throws Exception {
         final NetworkStats red = new NetworkStats(TEST_START, -1);
         final NetworkStats blue = new NetworkStats(TEST_START, 5)
@@ -459,6 +482,7 @@
         red.combineAllValues(blue);
     }
 
+    @Test
     public void testMigrateTun() throws Exception {
         final int tunUid = 10030;
         final String tunIface = "tun0";
@@ -556,6 +580,7 @@
     // interface by the vpn app before it's sent out of the underlying interface. The VPN app should
     // not be charged for the echoed data but it should still be charged for any extra data it sends
     // via the underlying interface.
+    @Test
     public void testMigrateTun_VpnAsLoopback() {
         final int tunUid = 10030;
         final String tunIface = "tun0";
diff --git a/core/tests/coretests/src/android/net/NetworkTest.java b/tests/net/java/android/net/NetworkTest.java
similarity index 89%
rename from core/tests/coretests/src/android/net/NetworkTest.java
rename to tests/net/java/android/net/NetworkTest.java
index 74b6d98..bacf986 100644
--- a/core/tests/coretests/src/android/net/NetworkTest.java
+++ b/tests/net/java/android/net/NetworkTest.java
@@ -16,13 +16,17 @@
 
 package android.net;
 
-import static android.test.MoreAsserts.assertNotEqual;
+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 android.net.LocalServerSocket;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.Network;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -32,13 +36,17 @@
 import java.net.InetAddress;
 import java.net.Inet6Address;
 import java.net.SocketException;
+import java.util.Objects;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class NetworkTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkTest {
     final Network mNetwork = new Network(99);
 
-    @SmallTest
+    @Test
     public void testBindSocketOfInvalidFdThrows() throws Exception {
 
         final FileDescriptor fd = new FileDescriptor();
@@ -50,7 +58,7 @@
         } catch (SocketException expected) {}
     }
 
-    @SmallTest
+    @Test
     public void testBindSocketOfNonSocketFdThrows() throws Exception {
         final File devNull = new File("/dev/null");
         assertTrue(devNull.canRead());
@@ -65,7 +73,7 @@
         } catch (SocketException expected) {}
     }
 
-    @SmallTest
+    @Test
     public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception {
         final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
         mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53);
@@ -77,7 +85,7 @@
         } catch (SocketException expected) {}
     }
 
-    @SmallTest
+    @Test
     public void testBindSocketOfLocalSocketThrows() throws Exception {
         final LocalSocket mLocalClient = new LocalSocket();
         mLocalClient.bind(new LocalSocketAddress("testClient"));
@@ -98,7 +106,7 @@
         } catch (SocketException expected) {}
     }
 
-    @SmallTest
+    @Test
     public void testZeroIsObviousForDebugging() {
         Network zero = new Network(0);
         assertEquals(0, zero.hashCode());
@@ -106,7 +114,7 @@
         assertEquals("0", zero.toString());
     }
 
-    @SmallTest
+    @Test
     public void testGetNetworkHandle() {
         Network one = new Network(1);
         Network two = new Network(2);
@@ -143,4 +151,8 @@
         assertEquals(8606370526L, two.getNetworkHandle());
         assertEquals(12901337822L, three.getNetworkHandle());
     }
+
+    private static <T> void assertNotEqual(T t1, T t2) {
+        assertFalse(Objects.equals(t1, t2));
+    }
 }
diff --git a/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java b/tests/net/java/android/net/StaticIpConfigurationTest.java
similarity index 89%
rename from core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
rename to tests/net/java/android/net/StaticIpConfigurationTest.java
index 59f780f..5bb5734 100644
--- a/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
+++ b/tests/net/java/android/net/StaticIpConfigurationTest.java
@@ -16,22 +16,26 @@
 
 package android.net;
 
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.RouteInfo;
-import android.net.StaticIpConfiguration;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import java.net.InetAddress;
 import java.util.HashSet;
+import java.util.Objects;
 
-import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-import static org.junit.Assert.*;
-
-
-public class StaticIpConfigurationTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StaticIpConfigurationTest {
 
     private static final String ADDRSTR = "192.0.2.2/25";
     private static final LinkAddress ADDR = new LinkAddress(ADDRSTR);
@@ -53,16 +57,8 @@
         assertEquals(0, s.dnsServers.size());
     }
 
-    private boolean isEqual(StaticIpConfiguration s1, StaticIpConfiguration s2) {
-        return s1.equals(s2);
-    }
-
-    private void assertEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
-        assertTrue(isEqual(s1, s2));
-    }
-
-    private void assertNotEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
-        assertFalse(isEqual(s1, s2));
+    private static <T> void assertNotEquals(T t1, T t2) {
+        assertFalse(Objects.equals(t1, t2));
     }
 
     private StaticIpConfiguration makeTestObject() {
@@ -76,13 +72,13 @@
         return s;
     }
 
-    @SmallTest
+    @Test
     public void testConstructor() {
         StaticIpConfiguration s = new StaticIpConfiguration();
         checkEmpty(s);
     }
 
-    @SmallTest
+    @Test
     public void testCopyAndClear() {
         StaticIpConfiguration empty = new StaticIpConfiguration((StaticIpConfiguration) null);
         checkEmpty(empty);
@@ -94,7 +90,7 @@
         assertEquals(empty, s2);
     }
 
-    @SmallTest
+    @Test
     public void testHashCodeAndEquals() {
         HashSet<Integer> hashCodes = new HashSet();
         hashCodes.add(0);
@@ -143,7 +139,7 @@
         assertNotEquals(s, s2);
     }
 
-    @SmallTest
+    @Test
     public void testToLinkProperties() {
         LinkProperties expected = new LinkProperties();
         expected.setInterfaceName(IFACE);
@@ -215,11 +211,10 @@
         return s2;
     }
 
-    @SmallTest
+    @Test
     public void testParceling() {
         StaticIpConfiguration s = makeTestObject();
         StaticIpConfiguration s2 = passThroughParcel(s);
         assertEquals(s, s2);
     }
 }
-
diff --git a/tests/net/java/android/net/UidRangeTest.java b/tests/net/java/android/net/UidRangeTest.java
index 0a56e1b..1d1013e 100644
--- a/tests/net/java/android/net/UidRangeTest.java
+++ b/tests/net/java/android/net/UidRangeTest.java
@@ -16,14 +16,20 @@
 
 package android.net;
 
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
-public class UidRangeTest extends TestCase {
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UidRangeTest {
 
     static {
         System.loadLibrary("frameworksnettestsjni");
@@ -33,7 +39,7 @@
     private static native int getStart(byte[] inParcel);
     private static native int getStop(byte[] inParcel);
 
-    @SmallTest
+    @Test
     public void testNativeParcelUnparcel() {
         UidRange original = new UidRange(1234, Integer.MAX_VALUE);
 
@@ -45,7 +51,7 @@
         assertArrayEquals(inParcel, outParcel);
     }
 
-    @SmallTest
+    @Test
     public void testIndividualNativeFields() {
         UidRange original = new UidRange(0x11115678, 0x22224321);
         byte[] originalBytes = marshall(original);
@@ -54,14 +60,14 @@
         assertEquals(original.stop, getStop(originalBytes));
     }
 
-    @SmallTest
+    @Test
     public void testSingleItemUidRangeAllowed() {
         new UidRange(123, 123);
         new UidRange(0, 0);
         new UidRange(Integer.MAX_VALUE, Integer.MAX_VALUE);
     }
 
-    @SmallTest
+    @Test
     public void testNegativeUidsDisallowed() {
         try {
             new UidRange(-2, 100);
@@ -76,7 +82,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testStopLessThanStartDisallowed() {
         final int x = 4195000;
         try {
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 5008a41..99a2ad9 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -16,6 +16,16 @@
 
 package android.net.apf;
 
+import static android.system.OsConstants.*;
+import static com.android.internal.util.BitUtils.bytesToBEInt;
+import static com.android.internal.util.BitUtils.put;
+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.atLeastOnce;
+import static org.mockito.Mockito.verify;
+
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
@@ -30,23 +40,22 @@
 import android.os.ConditionVariable;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.test.AndroidTestCase;
 import android.text.format.DateUtils;
-import android.test.suitebuilder.annotation.SmallTest;
-import static android.system.OsConstants.*;
 
 import com.android.frameworks.tests.net.R;
 import com.android.internal.util.HexDump;
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.put;
 
+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 static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.verify;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -69,14 +78,15 @@
  * Build, install and run with:
  *  runtest frameworks-net -c android.net.apf.ApfTest
  */
-public class ApfTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ApfTest {
     private static final int TIMEOUT_MS = 500;
 
     @Mock IpConnectivityLog mLog;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
         // Load up native shared library containing APF interpreter exposed via JNI.
         System.loadLibrary("frameworksnettestsjni");
@@ -161,7 +171,7 @@
      * generating bytecode for that program and running it through the
      * interpreter to verify it functions correctly.
      */
-    @SmallTest
+    @Test
     public void testApfInstructions() throws IllegalInstructionException {
         // Empty program should pass because having the program counter reach the
         // location immediately after the program indicates the packet should be
@@ -569,7 +579,7 @@
      * Generate some BPF programs, translate them to APF, then run APF and BPF programs
      * over packet traces and verify both programs filter out the same packets.
      */
-    @SmallTest
+    @Test
     public void testApfAgainstBpf() throws Exception {
         String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
                 "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
@@ -739,7 +749,7 @@
     private static final byte[] ANOTHER_IPV4_ADDR        = {10, 0, 0, 2};
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
-    @SmallTest
+    @Test
     public void testApfFilterIPv4() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
@@ -796,7 +806,7 @@
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @Test
     public void testApfFilterIPv6() throws Exception {
         final int[] ethTypeBlackList = {};
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
@@ -830,7 +840,7 @@
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @Test
     public void testApfFilterMulticast() throws Exception {
         final byte[] unicastIpv4Addr   = {(byte)192,0,2,63};
         final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
@@ -922,7 +932,7 @@
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @Test
     public void testApfFilter802_3() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
@@ -973,7 +983,7 @@
         apfFilter.shutdown();
     }
 
-    @SmallTest
+    @Test
     public void testApfFilterEthTypeBL() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
@@ -1058,7 +1068,7 @@
         assertDrop(program, garpReply());
     }
 
-    @SmallTest
+    @Test
     public void testApfFilterArp() throws Exception {
         final int[] ethTypeBlackList = {};
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
@@ -1136,7 +1146,7 @@
 
     // Test that when ApfFilter is shown the given packet, it generates a program to filter it
     // for the given lifetime.
-    private void testRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+    private void verifyRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
             ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
         // Verify new program generated if ApfFilter witnesses RA
         ipManagerCallback.resetApfProgramWait();
@@ -1181,7 +1191,7 @@
         ipManagerCallback.assertNoProgramUpdate();
     }
 
-    @SmallTest
+    @Test
     public void testApfFilterRa() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         final int[] ethTypeBlackList = {};
@@ -1212,7 +1222,7 @@
         basePacket.put(IPV6_ALL_NODES_ADDRESS);
         assertPass(program, basePacket.array());
 
-        testRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
+        verifyRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
 
         ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
@@ -1247,7 +1257,8 @@
         prefixOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
                 PREFIX_VALID_LIFETIME);
-        testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
+        verifyRaLifetime(
+                apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
         verifyRaEvent(new RaEvent(
                 ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
 
@@ -1259,7 +1270,7 @@
         rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         rdnssOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
-        testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
+        verifyRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
 
         ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
@@ -1270,7 +1281,7 @@
         routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         routeInfoOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
-        testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
+        verifyRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
 
         ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
@@ -1281,7 +1292,7 @@
         dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         dnsslOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
-        testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
+        verifyRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
 
         // Verify that current program filters all five RAs:
@@ -1301,12 +1312,12 @@
      * copy that resource into the app's data directory and return the path to it.
      */
     private String stageFile(int rawId) throws Exception {
-        File file = new File(getContext().getFilesDir(), "staged_file");
+        File file = new File(InstrumentationRegistry.getContext().getFilesDir(), "staged_file");
         new File(file.getParent()).mkdirs();
         InputStream in = null;
         OutputStream out = null;
         try {
-            in = getContext().getResources().openRawResource(rawId);
+            in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId);
             out = new FileOutputStream(file);
             Streams.copy(in, out);
         } finally {
@@ -1323,7 +1334,7 @@
         buffer.position(original);
     }
 
-    @SmallTest
+    @Test
     public void testRaParsing() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
@@ -1343,7 +1354,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testRaProcessing() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
@@ -1383,7 +1394,7 @@
     private native static boolean compareBpfApf(String filter, String pcap_filename,
             byte[] apf_program);
 
-    @SmallTest
+    @Test
     public void testBroadcastAddress() throws Exception {
         assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
         assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32));
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
index d79c312..050183c 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
@@ -16,23 +16,36 @@
 
 package android.net.dhcp;
 
+import static android.net.dhcp.DhcpPacket.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.net.DhcpResults;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
 import android.net.metrics.DhcpErrorEvent;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.system.OsConstants;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.internal.util.HexDump;
+
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Random;
-import junit.framework.TestCase;
 
-import static android.net.dhcp.DhcpPacket.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class DhcpPacketTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpPacketTest {
 
     private static Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
     private static Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
@@ -46,6 +59,7 @@
         return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
     }
 
+    @Before
     public void setUp() {
         DhcpPacket.testOverrideVendorId = "android-dhcp-???";
         DhcpPacket.testOverrideHostname = "android-01234567890abcde";
@@ -131,7 +145,7 @@
         assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
     }
 
-    @SmallTest
+    @Test
     public void testDomainName() throws Exception {
         byte[] nullByte = new byte[] { 0x00 };
         byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
@@ -186,7 +200,7 @@
         assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
     }
 
-    @SmallTest
+    @Test
     public void testLeaseTime() throws Exception {
         byte[] noLease = null;
         byte[] tooShortLease = new byte[] { 0x00, 0x00 };
@@ -234,7 +248,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testIpAddress() throws Exception {
         byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
         byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
@@ -278,11 +292,11 @@
         assertEquals(mtu, dhcpResults.mtu);
     }
 
-    @SmallTest
+    @Test
     public void testOffer1() throws Exception {
-        // TODO: Turn all of these into golden files. This will probably require modifying
-        // Android.mk appropriately, making this into an AndroidTestCase, and adding code to read
-        // the golden files from the test APK's assets via mContext.getAssets().
+        // TODO: Turn all of these into golden files. This will probably require using
+        // android.support.test.InstrumentationRegistry for obtaining a Context object
+        // to read such golden files, along with an appropriate Android.mk.
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // IP header.
             "451001480000000080118849c0a89003c0a89ff7" +
@@ -311,7 +325,7 @@
                 null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testOffer2() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // IP header.
@@ -343,7 +357,7 @@
         assertTrue(dhcpResults.hasMeteredHint());
     }
 
-    @SmallTest
+    @Test
     public void testBadIpPacket() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -358,7 +372,7 @@
         fail("Dhcp packet parsing should have failed");
     }
 
-    @SmallTest
+    @Test
     public void testBadDhcpPacket() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -377,7 +391,7 @@
         fail("Dhcp packet parsing should have failed");
     }
 
-    @SmallTest
+    @Test
     public void testBadTruncatedOffer() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -406,7 +420,7 @@
         fail("Dhcp packet parsing should have failed");
     }
 
-    @SmallTest
+    @Test
     public void testBadOfferWithoutACookie() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -437,7 +451,7 @@
         fail("Dhcp packet parsing should have failed");
     }
 
-    @SmallTest
+    @Test
     public void testOfferWithBadCookie() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -473,7 +487,7 @@
         assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
     }
 
-    @SmallTest
+    @Test
     public void testTruncatedOfferPackets() throws Exception {
         final byte[] packet = HexDump.hexStringToByteArray(
             // IP header.
@@ -507,7 +521,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testRandomPackets() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
@@ -547,7 +561,7 @@
                 null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testMtu() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // IP header.
@@ -583,7 +597,7 @@
         checkMtu(packet, 0, mtuBytes(-1));
     }
 
-    @SmallTest
+    @Test
     public void testBadHwaddrLength() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // IP header.
@@ -652,7 +666,7 @@
         assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
     }
 
-    @SmallTest
+    @Test
     public void testPadAndOverloadedOptionsOffer() throws Exception {
         // A packet observed in the real world that is interesting for two reasons:
         //
@@ -691,7 +705,7 @@
                 null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testBug2111() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // IP header.
@@ -721,7 +735,7 @@
                 "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testBug2136() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // Ethernet header.
@@ -754,7 +768,7 @@
                 "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testUdpServerAnySourcePort() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // Ethernet header.
@@ -789,7 +803,7 @@
                 "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testUdpInvalidDstPort() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // Ethernet header.
@@ -821,7 +835,7 @@
         } catch (ParseException expected) {}
     }
 
-    @SmallTest
+    @Test
     public void testMultipleRouters() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
             // Ethernet header.
@@ -854,7 +868,7 @@
                 null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
     }
 
-    @SmallTest
+    @Test
     public void testDiscoverPacket() throws Exception {
         short secs = 7;
         int transactionId = 0xdeadbeef;
diff --git a/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java b/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java
index 5deba27..6647760 100644
--- a/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java
+++ b/tests/net/java/android/net/netlink/NetlinkErrorMessageTest.java
@@ -19,20 +19,30 @@
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkErrorMessage;
 import android.net.netlink.NetlinkMessage;
 import android.net.netlink.StructNlMsgErr;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.util.Log;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
 import libcore.util.HexEncoding;
 
 
-public class NetlinkErrorMessageTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetlinkErrorMessageTest {
     private final String TAG = "NetlinkErrorMessageTest";
 
     // Hexadecimal representation of packet capture.
@@ -54,7 +64,7 @@
     public static final byte[] NLM_ERROR_OK =
             HexEncoding.decode(NLM_ERROR_OK_HEX.toCharArray(), false);
 
-    @SmallTest
+    @Test
     public void testParseNlmErrorOk() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(NLM_ERROR_OK);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
diff --git a/tests/net/java/android/net/netlink/NetlinkSocketTest.java b/tests/net/java/android/net/netlink/NetlinkSocketTest.java
index 78b3b70..bd36bac8 100644
--- a/tests/net/java/android/net/netlink/NetlinkSocketTest.java
+++ b/tests/net/java/android/net/netlink/NetlinkSocketTest.java
@@ -16,25 +16,35 @@
 
 package android.net.netlink;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.net.netlink.NetlinkSocket;
 import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
 import android.net.netlink.StructNlMsgHdr;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
 import android.system.OsConstants;
 import android.util.Log;
+
 import java.io.InterruptedIOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
 
 
-public class NetlinkSocketTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetlinkSocketTest {
     private final String TAG = "NetlinkSocketTest";
 
-    @SmallTest
+    @Test
     public void testBasicWorkingGetNeighborsQuery() throws Exception {
         NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
         assertNotNull(s);
@@ -93,7 +103,7 @@
         s.close();
     }
 
-    @SmallTest
+    @Test
     public void testRepeatedCloseCallsAreQuiet() throws Exception {
         // Create a working NetlinkSocket.
         NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
diff --git a/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java b/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java
index 029758e..c9fd74f 100644
--- a/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/tests/net/java/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -16,15 +16,19 @@
 
 package android.net.netlink;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkMessage;
 import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
 import android.net.netlink.StructNlMsgHdr;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.system.OsConstants;
 import android.util.Log;
-import libcore.util.HexEncoding;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -32,10 +36,15 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.Arrays;
-import junit.framework.TestCase;
 
+import org.junit.runner.RunWith;
+import org.junit.Test;
 
-public class RtNetlinkNeighborMessageTest extends TestCase {
+import libcore.util.HexEncoding;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RtNetlinkNeighborMessageTest {
     private final String TAG = "RtNetlinkNeighborMessageTest";
 
     // Hexadecimal representation of packet capture.
@@ -136,7 +145,7 @@
     public static final byte[] RTM_GETNEIGH_RESPONSE =
             HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
 
-    @SmallTest
+    @Test
     public void testParseRtmDelNeigh() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -163,7 +172,7 @@
         assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
     }
 
-    @SmallTest
+    @Test
     public void testParseRtmNewNeigh() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -190,7 +199,7 @@
         assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
     }
 
-    @SmallTest
+    @Test
     public void testParseRtmGetNeighResponse() {
         final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
@@ -215,7 +224,7 @@
         assertEquals(14, messageCount);
     }
 
-    @SmallTest
+    @Test
     public void testCreateRtmNewNeighMessage() {
         final int seqNo = 2635;
         final int ifIndex = 14;
diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
index 1aad453..29dfa4c 100644
--- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java
+++ b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
@@ -18,15 +18,19 @@
 
 import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE;
 import static android.system.OsConstants.*;
+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 android.os.Handler;
 import android.os.HandlerThread;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
 
-import libcore.io.IoBridge;
-
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -41,15 +45,21 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import junit.framework.TestCase;
+import org.junit.runner.RunWith;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
+import libcore.io.IoBridge;
 
 /**
  * Tests for BlockingSocketReader.
  *
  * @hide
  */
-public class BlockingSocketReaderTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BlockingSocketReaderTest {
     static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
     static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
 
@@ -103,7 +113,7 @@
         }
     };
 
-    @Override
+    @Before
     public void setUp() {
         resetLatch();
         mLocalSocket = null;
@@ -115,7 +125,7 @@
         mHandlerThread.start();
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         if (mReceiver != null) {
             mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
@@ -143,6 +153,7 @@
         sender.close();
     }
 
+    @Test
     public void testBasicWorking() throws Exception {
         final Handler h = mHandlerThread.getThreadHandler();
         mReceiver = new UdpLoopbackReader(h);
@@ -186,6 +197,7 @@
         public FileDescriptor createFd() { return null; }
     }
 
+    @Test
     public void testMinimalRecvBufSize() throws Exception {
         final Handler h = mHandlerThread.getThreadHandler();
 
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index dd679bc..38d3d74 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -17,18 +17,25 @@
 package android.net.util;
 
 import static android.net.util.NetworkConstants.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
 
 import libcore.util.HexEncoding;
 
-import junit.framework.TestCase;
-
-
 /**
  * Tests for ConnectivityPacketSummary.
  *
  * @hide
  */
-public class ConnectivityPacketSummaryTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConnectivityPacketSummaryTest {
     private static final byte[] MYHWADDR = {
         asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
     };
@@ -39,6 +46,7 @@
         return ConnectivityPacketSummary.summarize(MYHWADDR, bytes);
     }
 
+    @Test
     public void testParseICMPv6DADProbe() {
         final String packet =
                 // Ethernet
@@ -60,6 +68,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseICMPv6RS() {
         final String packet =
                 // Ethernet
@@ -81,6 +90,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseICMPv6RA() {
         final String packet =
                 // Ethernet
@@ -113,6 +123,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseICMPv6NS() {
         final String packet =
                 // Ethernet
@@ -135,6 +146,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testInvalidICMPv6NDLength() {
         final String packet =
                 // Ethernet
@@ -159,6 +171,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseICMPv6NA() {
         final String packet =
                 // Ethernet
@@ -179,6 +192,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseARPRequest() {
         final String packet =
                 // Ethernet
@@ -197,6 +211,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseARPReply() {
         final String packet =
                 // Ethernet
@@ -217,6 +232,7 @@
         assertEquals(expected, getSummary(packet));
     }
 
+    @Test
     public void testParseDHCPv4Discover() {
         final String packet =
                 // Ethernet
@@ -262,6 +278,7 @@
         assertTrue(getSummary(packet).startsWith(expectedPrefix));
     }
 
+    @Test
     public void testParseDHCPv4Offer() {
         final String packet =
                 // Ethernet
@@ -307,6 +324,7 @@
         assertTrue(getSummary(packet).startsWith(expectedPrefix));
     }
 
+    @Test
     public void testParseDHCPv4Request() {
         final String packet =
                 // Ethernet
@@ -354,6 +372,7 @@
         assertTrue(getSummary(packet).startsWith(expectedPrefix));
     }
 
+    @Test
     public void testParseDHCPv4Ack() {
         final String packet =
                 // Ethernet
diff --git a/tests/net/java/android/net/util/IpUtilsTest.java b/tests/net/java/android/net/util/IpUtilsTest.java
index c2d1608..8903bf9 100644
--- a/tests/net/java/android/net/util/IpUtilsTest.java
+++ b/tests/net/java/android/net/util/IpUtilsTest.java
@@ -16,15 +16,19 @@
 
 package android.net.util;
 
-import android.net.util.IpUtils;
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 
 import java.nio.ByteBuffer;
 
-import junit.framework.TestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
 
-
-public class IpUtilsTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpUtilsTest {
 
     private static final int IPV4_HEADER_LENGTH = 20;
     private static final int IPV6_HEADER_LENGTH = 40;
@@ -67,7 +71,7 @@
     //           "hello")
     // print JavaPacketDefinition(str(packet))
 
-    @SmallTest
+    @Test
     public void testIpv6TcpChecksum() throws Exception {
         // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80) /
         //           scapy.TCP(sport=12345, dport=7,
@@ -115,7 +119,7 @@
         assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
     }
 
-    @SmallTest
+    @Test
     public void testIpv4UdpChecksum() {
         // packet = (scapy.IP(src="192.0.2.1", dst="192.0.2.2", tos=0x40) /
         //           scapy.UDP(sport=32012, dport=4500) /
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index a423c2a..fb2bd79 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -24,12 +24,15 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import android.content.res.Resources;
 import android.net.NetworkStats;
 import android.net.TrafficStats;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.tests.net.R;
 
@@ -42,19 +45,23 @@
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
+import org.junit.runner.RunWith;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * Tests for {@link NetworkStatsFactory}.
  */
+@RunWith(AndroidJUnit4.class)
 @SmallTest
-public class NetworkStatsFactoryTest extends AndroidTestCase {
+public class NetworkStatsFactoryTest {
     private File mTestProc;
     private NetworkStatsFactory mFactory;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        mTestProc = new File(getContext().getFilesDir(), "proc");
+        mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc");
         if (mTestProc.exists()) {
             IoUtils.deleteContents(mTestProc);
         }
@@ -62,17 +69,16 @@
         mFactory = new NetworkStatsFactory(mTestProc);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         mFactory = null;
 
         if (mTestProc.exists()) {
             IoUtils.deleteContents(mTestProc);
         }
-
-        super.tearDown();
     }
 
+    @Test
     public void testNetworkStatsDetail() throws Exception {
         final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);
 
@@ -84,6 +90,7 @@
         assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L);
     }
 
+    @Test
     public void testKernelTags() throws Exception {
         assertEquals(0, kernelToTag("0x0000000000000000"));
         assertEquals(0x32, kernelToTag("0x0000003200000000"));
@@ -98,6 +105,7 @@
         assertEquals(TrafficStats.TAG_SYSTEM_DOWNLOAD, kernelToTag("0xffffff0100000000"));
     }
 
+    @Test
     public void testNetworkStatsWithSet() throws Exception {
         final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);
         assertEquals(70, stats.size());
@@ -106,6 +114,7 @@
         assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L);
     }
 
+    @Test
     public void testNetworkStatsSingle() throws Exception {
         stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));
 
@@ -116,6 +125,7 @@
         assertStatsEntry(stats, "test2", UID_ALL, SET_ALL, TAG_NONE, 1L, 2L, 3L, 4L);
     }
 
+    @Test
     public void testNetworkStatsXt() throws Exception {
         stageFile(R.raw.xt_qtaguid_iface_fmt_typical, file("net/xt_qtaguid/iface_stat_fmt"));
 
@@ -127,6 +137,7 @@
         assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L);
     }
 
+    @Test
     public void testDoubleClatAccounting() throws Exception {
         NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
 
@@ -161,6 +172,7 @@
         NetworkStatsFactory.noteStackedIface("v4-wlan0", null);
     }
 
+    @Test
     public void testDoubleClatAccounting100MBDownload() throws Exception {
         // Downloading 100mb from an ipv4 only destination in a foreground activity
 
@@ -197,7 +209,7 @@
         InputStream in = null;
         OutputStream out = null;
         try {
-            in = getContext().getResources().openRawResource(rawId);
+            in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId);
             out = new FileOutputStream(file);
             Streams.copy(in, out);
         } finally {
@@ -251,5 +263,4 @@
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
         assertEquals("unexpected txPackets", txPackets, entry.txPackets);
     }
-
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 335e6240..d9586c2 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -26,8 +26,13 @@
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.NetworkCapabilities.*;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 import static com.android.internal.util.TestUtils.waitForIdleHandler;
-
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
@@ -87,9 +92,10 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -105,7 +111,11 @@
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -123,13 +133,16 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 
+
 /**
  * Tests for {@link ConnectivityService}.
  *
  * Build, install and run with:
  *  runtest frameworks-net -c com.android.server.ConnectivityServiceTest
  */
-public class ConnectivityServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConnectivityServiceTest {
     private static final String TAG = "ConnectivityServiceTest";
 
     private static final int TIMEOUT_MS = 500;
@@ -141,6 +154,7 @@
     private MockNetworkAgent mWiFiNetworkAgent;
     private MockNetworkAgent mCellNetworkAgent;
     private MockNetworkAgent mEthernetNetworkAgent;
+    private Context mContext;
 
     // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
     // do not go through ConnectivityService but talk to netd directly, so they don't automatically
@@ -242,7 +256,7 @@
         waitForIdle(TIMEOUT_MS);
     }
 
-    @SmallTest
+    @Test
     public void testWaitForIdle() {
         final int attempts = 50;  // Causes the test to take about 200ms on bullhead-eng.
 
@@ -743,7 +757,8 @@
 
                 // Don't overlap test NetIDs with real NetIDs as binding sockets to real networks
                 // can have odd side-effects, like network validations succeeding.
-                final Network[] networks = ConnectivityManager.from(getContext()).getAllNetworks();
+                Context context = InstrumentationRegistry.getContext();
+                final Network[] networks = ConnectivityManager.from(context).getAllNetworks();
                 boolean overlaps = false;
                 for (Network network : networks) {
                     if (netId == network.netId) {
@@ -814,9 +829,9 @@
         fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
     }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
+        mContext = InstrumentationRegistry.getContext();
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
@@ -824,7 +839,7 @@
             Looper.prepare();
         }
 
-        mServiceContext = new MockContext(getContext());
+        mServiceContext = new MockContext(InstrumentationRegistry.getContext());
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
@@ -835,7 +850,7 @@
                 mock(IpConnectivityLog.class));
 
         mService.systemReady();
-        mCm = new WrappedConnectivityManager(getContext(), mService);
+        mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
         mCm.bindProcessToNetwork(null);
 
         // Ensure that the default setting for Captive Portals is used for most tests
@@ -843,6 +858,7 @@
         setMobileDataAlwaysOn(false);
     }
 
+    @After
     public void tearDown() throws Exception {
         setMobileDataAlwaysOn(false);
         if (mCellNetworkAgent != null) {
@@ -857,7 +873,6 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
-        super.tearDown();
     }
 
     private static int transportToLegacyType(int transport) {
@@ -931,6 +946,7 @@
         return cv;
     }
 
+    @Test
     public void testNetworkTypes() {
         // Ensure that our mocks for the networkAttributes config variable work as expected. If they
         // don't, then tests that depend on CONNECTIVITY_ACTION broadcasts for these network types
@@ -946,7 +962,7 @@
         assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET));
     }
 
-    @SmallTest
+    @Test
     public void testLingering() throws Exception {
         verifyNoNetwork();
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -987,7 +1003,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @Test
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -1022,7 +1038,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @Test
     public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
         // Test bringing up unvalidated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1048,7 +1064,7 @@
         verifyNoNetwork();
     }
 
-    @SmallTest
+    @Test
     public void testUnlingeringDoesNotValidate() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -1076,7 +1092,7 @@
                 NET_CAPABILITY_VALIDATED));
     }
 
-    @SmallTest
+    @Test
     public void testCellularOutscoresWeakWifi() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1102,7 +1118,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @Test
     public void testReapingNetwork() throws Exception {
         // Test bringing up WiFi without NET_CAPABILITY_INTERNET.
         // Expect it to be torn down immediately because it satisfies no requests.
@@ -1135,7 +1151,7 @@
         waitFor(cv);
     }
 
-    @SmallTest
+    @Test
     public void testCellularFallback() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1173,7 +1189,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @Test
     public void testWiFiFallback() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -1387,7 +1403,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testStateChangeNetworkCallbacks() throws Exception {
         final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
         final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
@@ -1477,7 +1493,7 @@
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
     }
 
-    @SmallTest
+    @Test
     public void testMultipleLingering() {
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED)
@@ -1705,7 +1721,7 @@
         mCm.unregisterNetworkCallback(trackDefaultCallback);
     }
 
-    @SmallTest
+    @Test
     public void testExplicitlySelected() {
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -1872,7 +1888,7 @@
         handlerThread.quit();
     }
 
-    @SmallTest
+    @Test
     public void testNetworkFactoryRequests() throws Exception {
         tryNetworkFactoryRequests(NET_CAPABILITY_MMS);
         tryNetworkFactoryRequests(NET_CAPABILITY_SUPL);
@@ -1892,7 +1908,7 @@
         // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed.
     }
 
-    @SmallTest
+    @Test
     public void testNoMutableNetworkRequests() throws Exception {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
         NetworkRequest request1 = new NetworkRequest.Builder()
@@ -1909,7 +1925,7 @@
         assertException(() -> { mCm.requestNetwork(request2, pendingIntent); }, expected);
     }
 
-    @SmallTest
+    @Test
     public void testMMSonWiFi() throws Exception {
         // Test bringing up cellular without MMS NetworkRequest gets reaped
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1948,7 +1964,7 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @Test
     public void testMMSonCell() throws Exception {
         // Test bringing up cellular without MMS
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -1977,7 +1993,7 @@
         verifyActiveNetwork(TRANSPORT_CELLULAR);
     }
 
-    @SmallTest
+    @Test
     public void testCaptivePortal() {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
@@ -2028,7 +2044,7 @@
         validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
     }
 
-    @SmallTest
+    @Test
     public void testCaptivePortalApp() {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
@@ -2074,7 +2090,7 @@
         mCm.unregisterNetworkCallback(captivePortalCallback);
     }
 
-    @SmallTest
+    @Test
     public void testAvoidOrIgnoreCaptivePortals() {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
@@ -2119,7 +2135,7 @@
         return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
     }
 
-    @SmallTest
+    @Test
     public void testNetworkSpecifier() {
         NetworkRequest rEmpty1 = newWifiRequestBuilder().build();
         NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build();
@@ -2180,7 +2196,7 @@
         assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cFoo, cBar);
     }
 
-    @SmallTest
+    @Test
     public void testInvalidNetworkSpecifier() {
         try {
             NetworkRequest.Builder builder = new NetworkRequest.Builder();
@@ -2241,7 +2257,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testNetworkSpecifierUidSpoofSecurityException() {
         class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
             @Override
@@ -2275,7 +2291,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testRegisterDefaultNetworkCallback() throws Exception {
         final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
@@ -2324,7 +2340,7 @@
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
     }
 
-    @SmallTest
+    @Test
     public void testAdditionalStateCallbacks() throws Exception {
         // File a network request for mobile.
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -2385,7 +2401,7 @@
         return nc.hasCapability(NET_CAPABILITY_FOREGROUND);
     }
 
-    @SmallTest
+    @Test
     public void testBackgroundNetworks() throws Exception {
         // Create a background request. We can't do this ourselves because ConnectivityService
         // doesn't have an API for it. So just turn on mobile data always on.
@@ -2554,7 +2570,7 @@
         return false;
     }
 
-    @SmallTest
+    @Test
     public void testMobileDataAlwaysOn() throws Exception {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
@@ -2619,7 +2635,7 @@
         handlerThread.quit();
     }
 
-    @SmallTest
+    @Test
     public void testAvoidBadWifiSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
         final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
@@ -2657,7 +2673,7 @@
         assertTrue(tracker.shouldNotifyWifiUnvalidated());
     }
 
-    @SmallTest
+    @Test
     public void testAvoidBadWifi() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
         final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
@@ -2784,7 +2800,7 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
-    @SmallTest
+    @Test
     public void testMeteredMultipathPreferenceSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
         final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
@@ -2808,7 +2824,7 @@
      * Validate that a satisfied network request does not trigger onUnavailable() once the
      * time-out period expires.
      */
-    @SmallTest
+    @Test
     public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() {
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
@@ -2828,7 +2844,7 @@
      * Validate that a satisfied network request followed by a disconnected (lost) network does
      * not trigger onUnavailable() once the time-out period expires.
      */
-    @SmallTest
+    @Test
     public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() {
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
@@ -2852,7 +2868,7 @@
      * callback is called when time-out expires. Then validate that if network request is
      * (somehow) satisfied - the callback isn't called later.
      */
-    @SmallTest
+    @Test
     public void testTimedoutNetworkRequest() {
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
@@ -2873,7 +2889,7 @@
      * Validate that when a network request is unregistered (cancelled), no posterior event can
      * trigger the callback.
      */
-    @SmallTest
+    @Test
     public void testNoCallbackAfterUnregisteredNetworkRequest() {
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
@@ -2981,7 +2997,7 @@
         return mWiFiNetworkAgent.getNetwork();
     }
 
-    @SmallTest
+    @Test
     public void testPacketKeepalives() throws Exception {
         InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
         InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
@@ -3105,7 +3121,7 @@
         callback3.expectStopped();
     }
 
-    @SmallTest
+    @Test
     public void testGetCaptivePortalServerUrl() throws Exception {
         String url = mCm.getCaptivePortalServerUrl();
         assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
@@ -3150,7 +3166,7 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
     }
 
-    @SmallTest
+    @Test
     public void testNetworkPinner() {
         NetworkRequest wifiRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_WIFI)
@@ -3210,7 +3226,7 @@
         assertPinnedToWifiWithCellDefault();
     }
 
-    @SmallTest
+    @Test
     public void testNetworkCallbackMaximum() {
         final int MAX_REQUESTS = 100;
         final int CALLBACKS = 90;
@@ -3302,7 +3318,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testNetworkInfoOfTypeNone() {
         ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
 
@@ -3342,7 +3358,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testDeprecatedAndUnsupportedOperations() throws Exception {
         final int TYPE_NONE = ConnectivityManager.TYPE_NONE;
         assertNull(mCm.getNetworkInfo(TYPE_NONE));
@@ -3363,7 +3379,7 @@
         assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported);
     }
 
-    @SmallTest
+    @Test
     public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() {
         final NetworkRequest networkRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_WIFI).build();
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index efc58cc..83ee361 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -36,6 +36,7 @@
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -48,11 +49,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 /** Unit tests for {@link IpSecService}. */
 @SmallTest
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 public class IpSecServiceTest {
 
     private static final int DROID_SPI = 0xD1201D;
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 77956be..354cf2f 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -16,24 +16,8 @@
 
 package com.android.server.connectivity;
 
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkMisc;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.format.DateUtils;
-import com.android.internal.R;
-import com.android.server.ConnectivityService;
-import com.android.server.connectivity.NetworkNotificationManager;
-import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import junit.framework.TestCase;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -44,7 +28,32 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.reset;
 
-public class LingerMonitorTest extends TestCase {
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkMisc;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
+import android.text.format.DateUtils;
+
+import com.android.internal.R;
+import com.android.server.ConnectivityService;
+import com.android.server.connectivity.NetworkNotificationManager;
+import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LingerMonitorTest {
     static final String CELLULAR = "CELLULAR";
     static final String WIFI     = "WIFI";
 
@@ -62,6 +71,7 @@
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
 
+    @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
@@ -71,7 +81,7 @@
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
 
-    @SmallTest
+    @Test
     public void testTransitions() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         NetworkAgentInfo nai1 = wifiNai(100);
@@ -81,7 +91,7 @@
         assertFalse(mMonitor.isNotificationEnabled(nai2, nai1));
     }
 
-    @SmallTest
+    @Test
     public void testNotificationOnLinger() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -92,7 +102,7 @@
         verifyNotification(from, to);
     }
 
-    @SmallTest
+    @Test
     public void testToastOnLinger() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
@@ -103,7 +113,7 @@
         verifyToast(from, to);
     }
 
-    @SmallTest
+    @Test
     public void testNotificationClearedAfterDisconnect() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -117,7 +127,7 @@
         verify(mNotifier, times(1)).clearNotification(100);
     }
 
-    @SmallTest
+    @Test
     public void testNotificationClearedAfterSwitchingBack() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -131,7 +141,7 @@
         verify(mNotifier, times(1)).clearNotification(100);
     }
 
-    @SmallTest
+    @Test
     public void testUniqueToast() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
@@ -149,7 +159,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testMultipleNotifications() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -168,7 +178,7 @@
         verifyNotification(wifi2, cell);
     }
 
-    @SmallTest
+    @Test
     public void testRateLimiting() throws InterruptedException {
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, LOW_RATE_LIMIT);
 
@@ -194,7 +204,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testDailyLimiting() throws InterruptedException {
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, LOW_DAILY_LIMIT, HIGH_RATE_LIMIT);
 
@@ -221,7 +231,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testUniqueNotification() {
         setNotificationSwitch(transition(WIFI, CELLULAR));
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
@@ -238,7 +248,7 @@
         verifyNotification(from, to);
     }
 
-    @SmallTest
+    @Test
     public void testIgnoreNeverValidatedNetworks() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -250,7 +260,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testIgnoreCurrentlyValidatedNetworks() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -262,7 +272,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testNoNotificationType() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch();
@@ -273,7 +283,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testNoTransitionToNotify() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_NONE);
         setNotificationSwitch(transition(WIFI, CELLULAR));
@@ -284,7 +294,7 @@
         verifyNoNotifications();
     }
 
-    @SmallTest
+    @Test
     public void testDifferentTransitionToNotify() {
         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
         setNotificationSwitch(transition(CELLULAR, WIFI));
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 911347c..125fe72 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -34,20 +34,29 @@
 import android.content.res.Resources;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-public class NetworkNotificationManagerTest extends TestCase {
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkNotificationManagerTest {
 
     static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
@@ -71,6 +80,7 @@
 
     NetworkNotificationManager mManager;
 
+    @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mCaptor = ArgumentCaptor.forClass(Notification.class);
@@ -87,7 +97,7 @@
         mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
     }
 
-    @SmallTest
+    @Test
     public void testNotificationsShownAndCleared() {
         final int NETWORK_ID_BASE = 100;
         List<NotificationType> types = Arrays.asList(NotificationType.values());
@@ -117,7 +127,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testNoInternetNotificationsNotShownForCellular() {
         mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false);
         mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false);
@@ -131,7 +141,7 @@
         verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any());
     }
 
-    @SmallTest
+    @Test
     public void testNotificationsNotShownIfNoInternetCapability() {
         mWifiNai.networkCapabilities = new NetworkCapabilities();
         mWifiNai.networkCapabilities .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
@@ -142,7 +152,7 @@
         verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
     }
 
-    @SmallTest
+    @Test
     public void testDuplicatedNotificationsNoInternetThenSignIn() {
         final int id = 101;
         final String tag = NetworkNotificationManager.tagFor(id);
@@ -164,7 +174,7 @@
         verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(SIGN_IN.eventId), any());
     }
 
-    @SmallTest
+    @Test
     public void testDuplicatedNotificationsSignInThenNoInternet() {
         final int id = 101;
         final String tag = NetworkNotificationManager.tagFor(id);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 4f18da7..fe396c3 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -20,6 +20,9 @@
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
 import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalMatchers.*;
 import static org.mockito.Mockito.*;
 
@@ -42,14 +45,17 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.R;
 import com.android.internal.net.VpnConfig;
 
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
 import org.mockito.Answers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
@@ -61,13 +67,16 @@
 import java.util.Map;
 import java.util.Set;
 
+
 /**
  * Tests for {@link Vpn}.
  *
  * Build, install and run with:
- *  runtest --path java/com/android/server/connectivity/VpnTest.java
+ *  runtest frameworks-net -c com.android.server.connectivity.VpnTest
  */
-public class VpnTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VpnTest {
     private static final String TAG = "VpnTest";
 
     // Mock users
@@ -106,7 +115,7 @@
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
@@ -130,7 +139,7 @@
         doNothing().when(mNetService).registerObserver(any());
     }
 
-    @SmallTest
+    @Test
     public void testRestrictedProfilesAreAddedToVpn() {
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
@@ -144,7 +153,7 @@
         })), ranges);
     }
 
-    @SmallTest
+    @Test
     public void testManagedProfilesAreNotAddedToVpn() {
         setMockedUsers(primaryUser, managedProfileA);
 
@@ -157,7 +166,7 @@
         })), ranges);
     }
 
-    @SmallTest
+    @Test
     public void testAddUserToVpnOnlyAddsOneUser() {
         setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
 
@@ -170,7 +179,7 @@
         })), ranges);
     }
 
-    @SmallTest
+    @Test
     public void testUidWhiteAndBlacklist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = UidRange.createForUser(primaryUser.id);
@@ -195,7 +204,7 @@
         })), disallow);
     }
 
-    @SmallTest
+    @Test
     public void testLockdownChangingPackage() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = UidRange.createForUser(primaryUser.id);
@@ -230,7 +239,7 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[3]);
     }
 
-    @SmallTest
+    @Test
     public void testLockdownAddingAProfile() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         setMockedUsers(primaryUser);
@@ -270,7 +279,7 @@
         }));
     }
 
-    @SmallTest
+    @Test
     public void testLockdownRuleRepeatability() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
 
@@ -293,7 +302,7 @@
         verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
     }
 
-    @SmallTest
+    @Test
     public void testLockdownRuleReversibility() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
 
@@ -322,7 +331,7 @@
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
     }
 
-    @SmallTest
+    @Test
     public void testIsAlwaysOnPackageSupported() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
 
@@ -356,7 +365,7 @@
         assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
     }
 
-    @SmallTest
+    @Test
     public void testNotificationShownForAlwaysOnApp() {
         final UserHandle userHandle = UserHandle.of(primaryUser.id);
         final Vpn vpn = createVpn(primaryUser.id);
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 4aeae70..9c4da1f 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -24,7 +24,6 @@
 import android.view.IWindowManager;
 import junit.framework.TestCase;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 /**
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 24068e9..756549c 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -17,13 +17,31 @@
 // ==========================================================
 // Build the host executable: protoc-gen-javastream
 // ==========================================================
-cc_binary_host {
-    name: "protoc-gen-javastream",
+cc_defaults {
+    name: "protoc-gen-stream-defaults",
     srcs: [
         "Errors.cpp",
         "string_utils.cpp",
-        "main.cpp",
+    ],
+}
+
+
+cc_binary_host {
+    name: "protoc-gen-javastream",
+    srcs: [
+        "java/main.cpp",
     ],
 
+    defaults: ["protoc-gen-stream-defaults"],
+    shared_libs: ["libprotoc"],
+}
+
+cc_binary_host {
+    name: "protoc-gen-cppstream",
+    srcs: [
+        "cpp/main.cpp",
+    ],
+
+    defaults: ["protoc-gen-stream-defaults"],
     shared_libs: ["libprotoc"],
 }
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 91c6b92..0cd9037 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -3,7 +3,7 @@
 #include <stdlib.h>
 
 namespace android {
-namespace javastream_proto {
+namespace stream_proto {
 
 Errors ERRORS;
 
@@ -82,6 +82,6 @@
     return m_errors.size() > 0;
 }
 
-} // namespace javastream_proto
+} // namespace stream_proto
 } // namespace android
 
diff --git a/tools/streaming_proto/Errors.h b/tools/streaming_proto/Errors.h
index 109195a..f14bbfd 100644
--- a/tools/streaming_proto/Errors.h
+++ b/tools/streaming_proto/Errors.h
@@ -4,7 +4,7 @@
 #include <vector>
 
 namespace android {
-namespace javastream_proto {
+namespace stream_proto {
 
 using namespace std;
 
@@ -44,5 +44,5 @@
 extern const int UNKNOWN_LINE;
 
 
-} // namespace javastream_proto
+} // namespace stream_proto
 } // namespace android
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
new file mode 100644
index 0000000..d4e1b7a
--- /dev/null
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -0,0 +1,273 @@
+#include "Errors.h"
+#include "string_utils.h"
+
+#include "google/protobuf/compiler/plugin.pb.h"
+#include "google/protobuf/io/zero_copy_stream_impl.h"
+#include "google/protobuf/text_format.h"
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+using namespace android::stream_proto;
+using namespace google::protobuf;
+using namespace google::protobuf::compiler;
+using namespace google::protobuf::io;
+using namespace std;
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+// Indent
+const string INDENT = "    ";
+
+/**
+ * See if this is the file for this request, and not one of the imported ones.
+ */
+static bool
+should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
+{
+    const int N = request.file_to_generate_size();
+    for (int i=0; i<N; i++) {
+        if (request.file_to_generate(i) == file) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static string
+make_filename(const FileDescriptorProto& file_descriptor)
+{
+    return file_descriptor.name() + ".h";
+}
+
+static string
+get_proto_type(const FieldDescriptorProto& field)
+{
+    switch (field.type()) {
+        case FieldDescriptorProto::TYPE_DOUBLE:
+            return "double";
+        case FieldDescriptorProto::TYPE_FLOAT:
+            return "float";
+        case FieldDescriptorProto::TYPE_INT64:
+            return "int64";
+        case FieldDescriptorProto::TYPE_UINT64:
+            return "uint64";
+        case FieldDescriptorProto::TYPE_INT32:
+            return "int32";
+        case FieldDescriptorProto::TYPE_FIXED64:
+            return "fixed64";
+        case FieldDescriptorProto::TYPE_FIXED32:
+            return "fixed32";
+        case FieldDescriptorProto::TYPE_BOOL:
+            return "bool";
+        case FieldDescriptorProto::TYPE_STRING:
+            return "string";
+        case FieldDescriptorProto::TYPE_GROUP:
+            return "group<unsupported!>";
+        case FieldDescriptorProto::TYPE_MESSAGE:
+            return field.type_name();
+        case FieldDescriptorProto::TYPE_BYTES:
+            return "bytes";
+        case FieldDescriptorProto::TYPE_UINT32:
+            return "uint32";
+        case FieldDescriptorProto::TYPE_ENUM:
+            return field.type_name();
+        case FieldDescriptorProto::TYPE_SFIXED32:
+            return "sfixed32";
+        case FieldDescriptorProto::TYPE_SFIXED64:
+            return "sfixed64";
+        case FieldDescriptorProto::TYPE_SINT32:
+            return "sint32";
+        case FieldDescriptorProto::TYPE_SINT64:
+            return "sint64";
+        default:
+            // won't happen
+            return "void";
+    }
+}
+
+static void
+write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
+{
+    const int N = enu.value_size();
+    text << indent << "// enum " << enu.name() << endl;
+    for (int i=0; i<N; i++) {
+        const EnumValueDescriptorProto& value = enu.value(i);
+        text << indent << "const uint32_t "
+                << make_constant_name(value.name())
+                << " = " << value.number() << ";" << endl;
+    }
+    text << endl;
+}
+
+static uint64_t
+get_field_id(const FieldDescriptorProto& field)
+{
+    // Number
+    uint64_t result = (uint64_t)field.number();
+
+    // Type
+    result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
+
+    // Count
+    if (field.options().packed()) {
+        result |= FIELD_COUNT_PACKED;
+    } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
+        result |= FIELD_COUNT_REPEATED;
+    } else {
+        result |= FIELD_COUNT_SINGLE;
+    }
+
+    return result;
+}
+
+static void
+write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
+{
+    string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
+            ? "optional " : "";
+    string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
+            ? "repeated " : "";
+    string proto_type = get_proto_type(field);
+    string packed_comment = field.options().packed()
+            ? " [packed=true]" : "";
+    text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
+            << field.name() << " = " << field.number() << packed_comment << ';' << endl;
+
+    text << indent << "const uint64_t " << make_constant_name(field.name()) << " = 0x";
+
+    ios::fmtflags fmt(text.flags());
+    text << setfill('0') << setw(16) << hex << get_field_id(field);
+    text.flags(fmt);
+
+    text << "LL;" << endl;
+
+    text << endl;
+}
+
+static void
+write_message(stringstream& text, const DescriptorProto& message, const string& indent)
+{
+    int N;
+    const string indented = indent + INDENT;
+
+    text << indent << "// message " << message.name() << endl;
+    text << indent << "class " << message.name() << " {" << endl;
+    text << indent << "public:" << endl;
+
+    // Enums
+    N = message.enum_type_size();
+    for (int i=0; i<N; i++) {
+        write_enum(text, message.enum_type(i), indented);
+    }
+
+    // Nested classes
+    N = message.nested_type_size();
+    for (int i=0; i<N; i++) {
+        write_message(text, message.nested_type(i), indented);
+    }
+
+    // Fields
+    N = message.field_size();
+    for (int i=0; i<N; i++) {
+        write_field(text, message.field(i), indented);
+    }
+
+    text << indent << "};" << endl;
+    text << endl;
+}
+
+static void
+write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+{
+    stringstream text;
+
+    text << "// Generated by protoc-gen-cppstream. DO NOT MODIFY." << endl;
+    text << "// source: " << file_descriptor.name() << endl << endl;
+
+    string header = "ANDROID_" + replace_string(file_descriptor.name(), '/', '_');
+    header = replace_string(header, '.', '_') + "_stream_h";
+    header = make_constant_name(header);
+
+    text << "#ifndef " << header << endl;
+    text << "#define " << header << endl;
+    text << endl;
+
+    vector<string> namespaces = split(file_descriptor.package(), '.');
+    for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+        text << "namespace " << *it << " {" << endl;
+    }
+    text << endl;
+
+    size_t N;
+    N = file_descriptor.enum_type_size();
+    for (size_t i=0; i<N; i++) {
+        write_enum(text, file_descriptor.enum_type(i), "");
+    }
+
+    N = file_descriptor.message_type_size();
+    for (size_t i=0; i<N; i++) {
+        write_message(text, file_descriptor.message_type(i), "");
+    }
+
+    for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+        text << "} // " << *it << endl;
+    }
+
+    text << endl;
+    text << "#endif // " << header << endl;
+
+    CodeGeneratorResponse::File* file_response = response->add_file();
+    file_response->set_name(make_filename(file_descriptor));
+    file_response->set_content(text.str());
+}
+
+int main(int argc, char const *argv[])
+{
+    (void)argc;
+    (void)argv;
+
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+    CodeGeneratorRequest request;
+    CodeGeneratorResponse response;
+
+    // Read the request
+    request.ParseFromIstream(&cin);
+
+    // Build the files we need.
+    const int N = request.proto_file_size();
+    for (int i=0; i<N; i++) {
+        const FileDescriptorProto& file_descriptor = request.proto_file(i);
+        if (should_generate_for_file(request, file_descriptor.name())) {
+            write_cpp_file(&response, file_descriptor);
+        }
+    }
+
+    // If we had errors, don't write the response. Print the errors and exit.
+    if (ERRORS.HasErrors()) {
+        ERRORS.Print();
+        return 1;
+    }
+
+    // If we didn't have errors, write the response and exit happily.
+    response.SerializeToOstream(&cout);
+
+    /* code */
+    return 0;
+}
\ No newline at end of file
diff --git a/tools/streaming_proto/main.cpp b/tools/streaming_proto/java/main.cpp
similarity index 99%
rename from tools/streaming_proto/main.cpp
rename to tools/streaming_proto/java/main.cpp
index 5b4ba04..b7d594b 100644
--- a/tools/streaming_proto/main.cpp
+++ b/tools/streaming_proto/java/main.cpp
@@ -12,7 +12,7 @@
 #include <sstream>
 #include <map>
 
-using namespace android::javastream_proto;
+using namespace android::stream_proto;
 using namespace google::protobuf;
 using namespace google::protobuf::compiler;
 using namespace google::protobuf::io;
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index cc738c4..bd34ab7 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -3,7 +3,7 @@
 #include <iostream>
 
 namespace android {
-namespace javastream_proto {
+namespace stream_proto {
 
 using namespace std;
 
@@ -89,7 +89,26 @@
     return result;
 }
 
-} // namespace javastream_proto
+vector<string>
+split(const string& str, const char delimiter)
+{
+    vector<string> result;
+    size_t base = 0, found = 0;
+    while (true) {
+        found = str.find_first_of(delimiter, base);
+        if (found != base) {
+            string part = str.substr(base, found - base);
+            if (!part.empty()) {
+                result.push_back(part);
+            }
+        }
+        if (found == str.npos) break;
+        base = found + 1;
+    }
+    return result;
+}
+
+} // namespace stream_proto
 } // namespace android
 
 
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index ffe83ca..03284d1 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -1,7 +1,8 @@
 #include <string>
+#include <vector>
 
 namespace android {
-namespace javastream_proto {
+namespace stream_proto {
 
 using namespace std;
 
@@ -26,7 +27,11 @@
  */
 string replace_string(const string& str, const char replace, const char with);
 
+/**
+ * Split a string to parts by delimiter.
+ */
+vector<string> split(const string& str, const char delimiter);
 
-} // namespace javastream_proto
+} // namespace stream_proto
 } // namespace android