Merge "Fix wrong parameter for StaticLayoutPerfTest"
diff --git a/Android.bp b/Android.bp
index dc51884..d5e04f9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -319,6 +319,10 @@
         "core/java/android/service/chooser/IChooserTargetResult.aidl",
         "core/java/android/service/resolver/IResolverRankerService.aidl",
         "core/java/android/service/resolver/IResolverRankerResult.aidl",
+        "core/java/android/service/textclassifier/ITextClassificationCallback.aidl",
+        "core/java/android/service/textclassifier/ITextClassifierService.aidl",
+        "core/java/android/service/textclassifier/ITextLinksCallback.aidl",
+        "core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityManager.aidl",
@@ -672,6 +676,7 @@
         "android.hardware.vibrator-V1.1-java-constants",
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
+        "android.hardware.usb.gadget-V1.0-java",
     ],
 
     // Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/Android.mk b/Android.mk
index 35b5f92..a78a01a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -821,39 +821,41 @@
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto/android/os)
+# Protos have lots of MissingOverride and similar.
+LOCAL_ERROR_PRONE_FLAGS := -XepDisableAllChecks
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # ==== hiddenapi lists =======================================
 
-# Copy blacklist and dark greylist over into the build folder.
+# Copy blacklist and light greylist over into the build folder.
 # This is for ART buildbots which need to mock these lists and have alternative
 # rules for building them. Other rules in the build system should depend on the
 # files in the build folder.
 
 $(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
                             $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
-                            $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-light-greylist.txt,\
+                            $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)))
 
-# Generate light greylist as private API minus (blacklist plus dark greylist).
+# Generate dark greylist as private API minus (blacklist plus light greylist).
 
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-                                               $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
-                                               $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
-	if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
-		echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+                                              $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+                                              $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+	if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+		echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
 		exit 1; \
 	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
 		echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
 		exit 2; \
-	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
-		echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+		echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
 		exit 3; \
 	fi
-	comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+	comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/api/current.txt b/api/current.txt
index 800412b..50579a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -969,7 +969,9 @@
     field public static final int orderingFromXml = 16843239; // 0x10101e7
     field public static final int orientation = 16842948; // 0x10100c4
     field public static final int outAnimation = 16843128; // 0x1010178
+    field public static final int outlineAmbientShadowColor = 16844162; // 0x1010582
     field public static final int outlineProvider = 16843960; // 0x10104b8
+    field public static final int outlineSpotShadowColor = 16844161; // 0x1010581
     field public static final int overScrollFooter = 16843459; // 0x10102c3
     field public static final int overScrollHeader = 16843458; // 0x10102c2
     field public static final int overScrollMode = 16843457; // 0x10102c1
@@ -6085,16 +6087,16 @@
     method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
-    method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+    method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
-    method public final boolean performGlobalAction(int);
+    method public boolean performGlobalAction(int);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
     method public boolean setRotation(int);
     method public void setRunAsMonkey(boolean);
-    method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+    method public void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
     method public android.graphics.Bitmap takeScreenshot();
     method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
     field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
@@ -6498,7 +6500,7 @@
     method public boolean isUsingUnifiedPassword(android.content.ComponentName);
     method public void lockNow();
     method public void lockNow(int);
-    method public boolean logoutUser(android.content.ComponentName);
+    method public int logoutUser(android.content.ComponentName);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -6583,8 +6585,8 @@
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
-    method public boolean startUserInBackground(android.content.ComponentName, android.os.UserHandle);
-    method public boolean stopUser(android.content.ComponentName, android.os.UserHandle);
+    method public int startUserInBackground(android.content.ComponentName, android.os.UserHandle);
+    method public int stopUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
     method public void transferOwnership(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void uninstallAllUserCaCerts(android.content.ComponentName);
@@ -6698,6 +6700,11 @@
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+    field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
+    field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
+    field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
+    field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
@@ -7161,7 +7168,8 @@
     method public android.net.Uri getUri();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
-    field public static final java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
+    field public static final java.lang.String EXTRA_RANGE_VALUE = "android.app.slice.extra.RANGE_VALUE";
+    field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
     field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     field public static final java.lang.String HINT_ACTIONS = "actions";
     field public static final java.lang.String HINT_CALLER_NEEDED = "caller_needed";
@@ -7181,7 +7189,8 @@
     field public static final java.lang.String SUBTYPE_MAX = "max";
     field public static final java.lang.String SUBTYPE_MESSAGE = "message";
     field public static final java.lang.String SUBTYPE_PRIORITY = "priority";
-    field public static final java.lang.String SUBTYPE_SLIDER = "slider";
+    field public static final java.lang.String SUBTYPE_RANGE = "range";
+    field public static final deprecated java.lang.String SUBTYPE_SLIDER = "slider";
     field public static final java.lang.String SUBTYPE_SOURCE = "source";
     field public static final java.lang.String SUBTYPE_TOGGLE = "toggle";
     field public static final java.lang.String SUBTYPE_VALUE = "value";
@@ -7243,6 +7252,7 @@
     method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
     method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
+    method public android.net.Uri mapIntentToUri(android.content.Intent);
     method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
     method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
     method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
@@ -11606,15 +11616,15 @@
 
   public final class AssetManager implements java.lang.AutoCloseable {
     method public void close();
-    method public java.lang.String[] getLocales();
-    method public java.lang.String[] list(java.lang.String) throws java.io.IOException;
-    method public java.io.InputStream open(java.lang.String) throws java.io.IOException;
-    method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
-    method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
-    method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
-    method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
-    method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
-    method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
+    method public final java.lang.String[] getLocales();
+    method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
+    method public final java.io.InputStream open(java.lang.String) throws java.io.IOException;
+    method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
+    method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
+    method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
+    method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
+    method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
+    method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
     field public static final int ACCESS_BUFFER = 3; // 0x3
     field public static final int ACCESS_RANDOM = 1; // 0x1
     field public static final int ACCESS_STREAMING = 2; // 0x2
@@ -11622,15 +11632,9 @@
   }
 
   public final class AssetManager.AssetInputStream extends java.io.InputStream {
-    method public final int available() throws java.io.IOException;
-    method public final void close() throws java.io.IOException;
-    method public final void mark(int);
-    method public final boolean markSupported();
-    method public final int read() throws java.io.IOException;
-    method public final int read(byte[]) throws java.io.IOException;
-    method public final int read(byte[], int, int) throws java.io.IOException;
-    method public final void reset() throws java.io.IOException;
-    method public final long skip(long) throws java.io.IOException;
+    method public void mark(int);
+    method public int read() throws java.io.IOException;
+    method public void reset() throws java.io.IOException;
   }
 
   public class ColorStateList implements android.os.Parcelable {
@@ -12392,7 +12396,7 @@
     method public java.util.List<android.util.Pair<java.lang.String, java.lang.String>> getAttachedDbs();
     method public long getMaximumSize();
     method public long getPageSize();
-    method public final java.lang.String getPath();
+    method public java.lang.String getPath();
     method public deprecated java.util.Map<java.lang.String, java.lang.String> getSyncedTables();
     method public int getVersion();
     method public boolean inTransaction();
@@ -13044,34 +13048,36 @@
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
+    method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
     method public int describeContents();
     method public void eraseColor(int);
     method public android.graphics.Bitmap extractAlpha();
     method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
-    method public final int getAllocationByteCount();
-    method public final int getByteCount();
-    method public final android.graphics.ColorSpace getColorSpace();
-    method public final android.graphics.Bitmap.Config getConfig();
+    method public int getAllocationByteCount();
+    method public int getByteCount();
+    method public android.graphics.ColorSpace getColorSpace();
+    method public android.graphics.Bitmap.Config getConfig();
     method public int getDensity();
     method public int getGenerationId();
-    method public final int getHeight();
+    method public int getHeight();
     method public byte[] getNinePatchChunk();
     method public int getPixel(int, int);
     method public void getPixels(int[], int, int, int, int, int, int);
-    method public final int getRowBytes();
+    method public int getRowBytes();
     method public int getScaledHeight(android.graphics.Canvas);
     method public int getScaledHeight(android.util.DisplayMetrics);
     method public int getScaledHeight(int);
     method public int getScaledWidth(android.graphics.Canvas);
     method public int getScaledWidth(android.util.DisplayMetrics);
     method public int getScaledWidth(int);
-    method public final int getWidth();
-    method public final boolean hasAlpha();
-    method public final boolean hasMipMap();
-    method public final boolean isMutable();
-    method public final boolean isPremultiplied();
-    method public final boolean isRecycled();
+    method public int getWidth();
+    method public boolean hasAlpha();
+    method public boolean hasMipMap();
+    method public boolean isMutable();
+    method public boolean isPremultiplied();
+    method public boolean isRecycled();
     method public void prepareToDraw();
     method public void reconfigure(int, int, android.graphics.Bitmap.Config);
     method public void recycle();
@@ -13079,11 +13085,11 @@
     method public void setConfig(android.graphics.Bitmap.Config);
     method public void setDensity(int);
     method public void setHasAlpha(boolean);
-    method public final void setHasMipMap(boolean);
+    method public void setHasMipMap(boolean);
     method public void setHeight(int);
     method public void setPixel(int, int, int);
     method public void setPixels(int[], int, int, int, int, int, int);
-    method public final void setPremultiplied(boolean);
+    method public void setPremultiplied(boolean);
     method public void setWidth(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.graphics.Bitmap> CREATOR;
@@ -13155,7 +13161,7 @@
     method public android.graphics.Bitmap decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options);
     method public int getHeight();
     method public int getWidth();
-    method public final boolean isRecycled();
+    method public boolean isRecycled();
     method public static android.graphics.BitmapRegionDecoder newInstance(byte[], int, int, boolean) throws java.io.IOException;
     method public static android.graphics.BitmapRegionDecoder newInstance(java.io.FileDescriptor, boolean) throws java.io.IOException;
     method public static android.graphics.BitmapRegionDecoder newInstance(java.io.InputStream, boolean) throws java.io.IOException;
@@ -14101,6 +14107,7 @@
     method public void endRecording();
     method public int getHeight();
     method public int getWidth();
+    method public boolean requiresHardwareAcceleration();
     method public deprecated void writeToStream(java.io.OutputStream);
   }
 
@@ -14215,22 +14222,22 @@
     ctor public Rect();
     ctor public Rect(int, int, int, int);
     ctor public Rect(android.graphics.Rect);
-    method public final int centerX();
-    method public final int centerY();
+    method public int centerX();
+    method public int centerY();
     method public boolean contains(int, int);
     method public boolean contains(int, int, int, int);
     method public boolean contains(android.graphics.Rect);
     method public int describeContents();
-    method public final float exactCenterX();
-    method public final float exactCenterY();
+    method public float exactCenterX();
+    method public float exactCenterY();
     method public java.lang.String flattenToString();
-    method public final int height();
+    method public int height();
     method public void inset(int, int);
     method public boolean intersect(int, int, int, int);
     method public boolean intersect(android.graphics.Rect);
     method public boolean intersects(int, int, int, int);
     method public static boolean intersects(android.graphics.Rect, android.graphics.Rect);
-    method public final boolean isEmpty();
+    method public boolean isEmpty();
     method public void offset(int, int);
     method public void offsetTo(int, int);
     method public void readFromParcel(android.os.Parcel);
@@ -14244,7 +14251,7 @@
     method public void union(int, int, int, int);
     method public void union(android.graphics.Rect);
     method public void union(int, int);
-    method public final int width();
+    method public int width();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.graphics.Rect> CREATOR;
     field public int bottom;
@@ -15813,9 +15820,7 @@
   }
 
   public static final class CameraCharacteristics.Key<T> {
-    method public final boolean equals(java.lang.Object);
     method public java.lang.String getName();
-    method public final int hashCode();
   }
 
   public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
@@ -16177,9 +16182,7 @@
   }
 
   public static final class CaptureRequest.Key<T> {
-    method public final boolean equals(java.lang.Object);
     method public java.lang.String getName();
-    method public final int hashCode();
   }
 
   public class CaptureResult extends android.hardware.camera2.CameraMetadata {
@@ -16272,9 +16275,7 @@
   }
 
   public static final class CaptureResult.Key<T> {
-    method public final boolean equals(java.lang.Object);
     method public java.lang.String getName();
-    method public final int hashCode();
   }
 
   public final class DngCreator implements java.lang.AutoCloseable {
@@ -16368,7 +16369,7 @@
     method public void addSurface(android.view.Surface);
     method public int describeContents();
     method public void enableSurfaceSharing();
-    method public static int getMaxSharedSurfaceCount();
+    method public int getMaxSharedSurfaceCount();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
@@ -16386,7 +16387,7 @@
     method public float getComponent(int);
     method public float getGreenEven();
     method public float getGreenOdd();
-    method public final float getRed();
+    method public float getRed();
     field public static final int BLUE = 3; // 0x3
     field public static final int COUNT = 4; // 0x4
     field public static final int GREEN_EVEN = 1; // 0x1
@@ -16414,16 +16415,16 @@
     method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size);
     method public android.util.Size[] getHighSpeedVideoSizes();
     method public android.util.Size[] getHighSpeedVideoSizesFor(android.util.Range<java.lang.Integer>);
-    method public final int[] getInputFormats();
+    method public int[] getInputFormats();
     method public android.util.Size[] getInputSizes(int);
-    method public final int[] getOutputFormats();
+    method public int[] getOutputFormats();
     method public long getOutputMinFrameDuration(int, android.util.Size);
     method public <T> long getOutputMinFrameDuration(java.lang.Class<T>, android.util.Size);
     method public <T> android.util.Size[] getOutputSizes(java.lang.Class<T>);
     method public android.util.Size[] getOutputSizes(int);
     method public long getOutputStallDuration(int, android.util.Size);
     method public <T> long getOutputStallDuration(java.lang.Class<T>, android.util.Size);
-    method public final int[] getValidOutputFormatsForInput(int);
+    method public int[] getValidOutputFormatsForInput(int);
     method public boolean isOutputSupportedFor(int);
     method public static <T> boolean isOutputSupportedFor(java.lang.Class<T>);
     method public boolean isOutputSupportedFor(android.view.Surface);
@@ -16731,12 +16732,12 @@
 
   public final class UCharacter implements android.icu.lang.UCharacterEnums.ECharacterCategory android.icu.lang.UCharacterEnums.ECharacterDirection {
     method public static int charCount(int);
-    method public static final int codePointAt(java.lang.CharSequence, int);
-    method public static final int codePointAt(char[], int);
-    method public static final int codePointAt(char[], int, int);
-    method public static final int codePointBefore(java.lang.CharSequence, int);
-    method public static final int codePointBefore(char[], int);
-    method public static final int codePointBefore(char[], int, int);
+    method public static int codePointAt(java.lang.CharSequence, int);
+    method public static int codePointAt(char[], int);
+    method public static int codePointAt(char[], int, int);
+    method public static int codePointBefore(java.lang.CharSequence, int);
+    method public static int codePointBefore(char[], int);
+    method public static int codePointBefore(char[], int, int);
     method public static int codePointCount(java.lang.CharSequence, int, int);
     method public static int codePointCount(char[], int, int);
     method public static int digit(int, int);
@@ -16744,7 +16745,7 @@
     method public static int foldCase(int, boolean);
     method public static java.lang.String foldCase(java.lang.String, boolean);
     method public static int foldCase(int, int);
-    method public static final java.lang.String foldCase(java.lang.String, int);
+    method public static java.lang.String foldCase(java.lang.String, int);
     method public static char forDigit(int, int);
     method public static android.icu.util.VersionInfo getAge(int);
     method public static int getBidiPairedBracket(int);
@@ -16796,8 +16797,8 @@
     method public static boolean isPrintable(int);
     method public static boolean isSpaceChar(int);
     method public static boolean isSupplementary(int);
-    method public static final boolean isSupplementaryCodePoint(int);
-    method public static final boolean isSurrogatePair(char, char);
+    method public static boolean isSupplementaryCodePoint(int);
+    method public static boolean isSurrogatePair(char, char);
     method public static boolean isTitleCase(int);
     method public static boolean isUAlphabetic(int);
     method public static boolean isULowercase(int);
@@ -16806,13 +16807,13 @@
     method public static boolean isUnicodeIdentifierPart(int);
     method public static boolean isUnicodeIdentifierStart(int);
     method public static boolean isUpperCase(int);
-    method public static final boolean isValidCodePoint(int);
+    method public static boolean isValidCodePoint(int);
     method public static boolean isWhitespace(int);
     method public static int offsetByCodePoints(java.lang.CharSequence, int, int);
     method public static int offsetByCodePoints(char[], int, int, int, int);
-    method public static final int toChars(int, char[], int);
-    method public static final char[] toChars(int);
-    method public static final int toCodePoint(char, char);
+    method public static int toChars(int, char[], int);
+    method public static char[] toChars(int);
+    method public static int toCodePoint(char, char);
     method public static int toLowerCase(int);
     method public static java.lang.String toLowerCase(java.lang.String);
     method public static java.lang.String toLowerCase(java.util.Locale, java.lang.String);
@@ -17102,7 +17103,7 @@
   }
 
   public static final class UCharacter.UnicodeBlock extends java.lang.Character.Subset {
-    method public static final android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
+    method public static android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
     method public int getID();
     method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
     method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
@@ -17909,20 +17910,20 @@
   }
 
   public final class UScript {
-    method public static final boolean breaksBetweenLetters(int);
-    method public static final int[] getCode(java.util.Locale);
-    method public static final int[] getCode(android.icu.util.ULocale);
-    method public static final int[] getCode(java.lang.String);
-    method public static final int getCodeFromName(java.lang.String);
-    method public static final java.lang.String getName(int);
-    method public static final java.lang.String getSampleString(int);
-    method public static final int getScript(int);
-    method public static final int getScriptExtensions(int, java.util.BitSet);
-    method public static final java.lang.String getShortName(int);
-    method public static final android.icu.lang.UScript.ScriptUsage getUsage(int);
-    method public static final boolean hasScript(int, int);
-    method public static final boolean isCased(int);
-    method public static final boolean isRightToLeft(int);
+    method public static boolean breaksBetweenLetters(int);
+    method public static int[] getCode(java.util.Locale);
+    method public static int[] getCode(android.icu.util.ULocale);
+    method public static int[] getCode(java.lang.String);
+    method public static int getCodeFromName(java.lang.String);
+    method public static java.lang.String getName(int);
+    method public static java.lang.String getSampleString(int);
+    method public static int getScript(int);
+    method public static int getScriptExtensions(int, java.util.BitSet);
+    method public static java.lang.String getShortName(int);
+    method public static android.icu.lang.UScript.ScriptUsage getUsage(int);
+    method public static boolean hasScript(int, int);
+    method public static boolean isCased(int);
+    method public static boolean isRightToLeft(int);
     field public static final int ADLAM = 167; // 0xa7
     field public static final int AFAKA = 147; // 0x93
     field public static final int AHOM = 161; // 0xa1
@@ -18337,14 +18338,14 @@
     method public deprecated int hashCode();
     method public int next();
     method public int previous();
-    method public static final int primaryOrder(int);
+    method public static int primaryOrder(int);
     method public void reset();
-    method public static final int secondaryOrder(int);
+    method public static int secondaryOrder(int);
     method public void setOffset(int);
     method public void setText(java.lang.String);
     method public void setText(android.icu.text.UCharacterIterator);
     method public void setText(java.text.CharacterIterator);
-    method public static final int tertiaryOrder(int);
+    method public static int tertiaryOrder(int);
     field public static final int IGNORABLE = 0; // 0x0
     field public static final int NULLORDER = -1; // 0xffffffff
   }
@@ -19571,7 +19572,7 @@
     method public boolean isUpperCaseFirst();
     method public void setAlternateHandlingDefault();
     method public void setAlternateHandlingShifted(boolean);
-    method public final void setCaseFirstDefault();
+    method public void setCaseFirstDefault();
     method public void setCaseLevel(boolean);
     method public void setCaseLevelDefault();
     method public void setDecompositionDefault();
@@ -20544,11 +20545,11 @@
   public final class LocaleData {
     method public static android.icu.util.VersionInfo getCLDRVersion();
     method public java.lang.String getDelimiter(int);
-    method public static final android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
-    method public static final android.icu.util.LocaleData getInstance();
-    method public static final android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
+    method public static android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
+    method public static android.icu.util.LocaleData getInstance();
+    method public static android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
     method public boolean getNoSubstitute();
-    method public static final android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
+    method public static android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
     method public void setNoSubstitute(boolean);
     field public static final int ALT_QUOTATION_END = 3; // 0x3
     field public static final int ALT_QUOTATION_START = 2; // 0x2
@@ -21948,6 +21949,7 @@
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
     field public static final int ENCODING_E_AC3 = 6; // 0x6
+    field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
     field public static final int ENCODING_IEC61937 = 13; // 0xd
     field public static final int ENCODING_INVALID = 0; // 0x0
     field public static final int ENCODING_MP3 = 9; // 0x9
@@ -21978,6 +21980,7 @@
     method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
+    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
     method public java.lang.String getProperty(java.lang.String);
@@ -22151,11 +22154,26 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
   }
 
+  public final class AudioPresentation {
+    method public java.util.Map<java.util.Locale, java.lang.String> getLabels();
+    method public java.util.Locale getLocale();
+    method public int getMasteringIndication();
+    method public boolean hasAudioDescription();
+    method public boolean hasDialogueEnhancement();
+    method public boolean hasSpokenSubtitles();
+    field public static final int MASTERED_FOR_3D = 3; // 0x3
+    field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
+    field public static final int MASTERED_FOR_STEREO = 1; // 0x1
+    field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
+    field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+  }
+
   public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
+    method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -22316,6 +22334,7 @@
     method public int setPlaybackRate(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+    method public int setPresentation(android.media.AudioPresentation);
     method protected deprecated void setState(int);
     method public deprecated int setStereoVolume(float, float);
     method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -22820,40 +22839,40 @@
     method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
     method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException;
     method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException;
-    method public final android.view.Surface createInputSurface();
+    method public android.view.Surface createInputSurface();
     method public static android.view.Surface createPersistentInputSurface();
-    method public final int dequeueInputBuffer(long);
-    method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
+    method public int dequeueInputBuffer(long);
+    method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
     method protected void finalize();
-    method public final void flush();
+    method public void flush();
     method public android.media.MediaCodecInfo getCodecInfo();
     method public java.nio.ByteBuffer getInputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getInputBuffers();
-    method public final android.media.MediaFormat getInputFormat();
+    method public android.media.MediaFormat getInputFormat();
     method public android.media.Image getInputImage(int);
     method public android.os.PersistableBundle getMetrics();
-    method public final java.lang.String getName();
+    method public java.lang.String getName();
     method public java.nio.ByteBuffer getOutputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
-    method public final android.media.MediaFormat getOutputFormat();
-    method public final android.media.MediaFormat getOutputFormat(int);
+    method public android.media.MediaFormat getOutputFormat();
+    method public android.media.MediaFormat getOutputFormat(int);
     method public android.media.Image getOutputImage(int);
-    method public final void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
-    method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
-    method public final void release();
-    method public final void releaseOutputBuffer(int, boolean);
-    method public final void releaseOutputBuffer(int, long);
-    method public final void reset();
+    method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
+    method public void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
+    method public void release();
+    method public void releaseOutputBuffer(int, boolean);
+    method public void releaseOutputBuffer(int, long);
+    method public void reset();
     method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
     method public void setCallback(android.media.MediaCodec.Callback);
     method public void setInputSurface(android.view.Surface);
     method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler);
     method public void setOutputSurface(android.view.Surface);
-    method public final void setParameters(android.os.Bundle);
-    method public final void setVideoScalingMode(int);
-    method public final void signalEndOfInputStream();
-    method public final void start();
-    method public final void stop();
+    method public void setParameters(android.os.Bundle);
+    method public void setVideoScalingMode(int);
+    method public void signalEndOfInputStream();
+    method public void start();
+    method public void stop();
     field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
     field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
     field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -22947,10 +22966,10 @@
   }
 
   public final class MediaCodecInfo {
-    method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
-    method public final java.lang.String getName();
-    method public final java.lang.String[] getSupportedTypes();
-    method public final boolean isEncoder();
+    method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
+    method public java.lang.String getName();
+    method public java.lang.String[] getSupportedTypes();
+    method public boolean isEncoder();
   }
 
   public static final class MediaCodecInfo.AudioCapabilities {
@@ -22970,9 +22989,9 @@
     method public int getMaxSupportedInstances();
     method public java.lang.String getMimeType();
     method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
-    method public final boolean isFeatureRequired(java.lang.String);
-    method public final boolean isFeatureSupported(java.lang.String);
-    method public final boolean isFormatSupported(android.media.MediaFormat);
+    method public boolean isFeatureRequired(java.lang.String);
+    method public boolean isFeatureSupported(java.lang.String);
+    method public boolean isFormatSupported(android.media.MediaFormat);
     field public static final deprecated int COLOR_Format12bitRGB444 = 3; // 0x3
     field public static final deprecated int COLOR_Format16bitARGB1555 = 5; // 0x5
     field public static final deprecated int COLOR_Format16bitARGB4444 = 4; // 0x4
@@ -23230,11 +23249,11 @@
 
   public final class MediaCodecList {
     ctor public MediaCodecList(int);
-    method public final java.lang.String findDecoderForFormat(android.media.MediaFormat);
-    method public final java.lang.String findEncoderForFormat(android.media.MediaFormat);
-    method public static final deprecated int getCodecCount();
-    method public static final deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
-    method public final android.media.MediaCodecInfo[] getCodecInfos();
+    method public java.lang.String findDecoderForFormat(android.media.MediaFormat);
+    method public java.lang.String findEncoderForFormat(android.media.MediaFormat);
+    method public static deprecated int getCodecCount();
+    method public static deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
+    method public android.media.MediaCodecInfo[] getCodecInfos();
     field public static final int ALL_CODECS = 1; // 0x1
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
@@ -23242,10 +23261,10 @@
   public final class MediaCrypto {
     ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
     method protected void finalize();
-    method public static final boolean isCryptoSchemeSupported(java.util.UUID);
-    method public final void release();
-    method public final boolean requiresSecureDecoderComponent(java.lang.String);
-    method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
+    method public static boolean isCryptoSchemeSupported(java.util.UUID);
+    method public void release();
+    method public boolean requiresSecureDecoderComponent(java.lang.String);
+    method public void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
   }
 
   public final class MediaCryptoException extends java.lang.Exception {
@@ -23261,10 +23280,10 @@
   public final class MediaDescrambler implements java.lang.AutoCloseable {
     ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
     method public void close();
-    method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
+    method public int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
     method protected void finalize();
-    method public final boolean requiresSecureDecoderComponent(java.lang.String);
-    method public final void setMediaCasSession(android.media.MediaCas.Session);
+    method public boolean requiresSecureDecoderComponent(java.lang.String);
+    method public void setMediaCasSession(android.media.MediaCas.Session);
   }
 
   public class MediaDescription implements android.os.Parcelable {
@@ -23320,8 +23339,8 @@
     method public java.util.List<byte[]> getSecureStopIds();
     method public java.util.List<byte[]> getSecureStops();
     method public int getSecurityLevel(byte[]);
-    method public static final boolean isCryptoSchemeSupported(java.util.UUID);
-    method public static final boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
+    method public static boolean isCryptoSchemeSupported(java.util.UUID);
+    method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
     method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
     method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
@@ -23472,6 +23491,7 @@
     ctor public MediaExtractor();
     method public boolean advance();
     method protected void finalize();
+    method public java.util.List<android.media.AudioPresentation> getAudioPresentations(int);
     method public long getCachedDuration();
     method public android.media.MediaExtractor.CasInfo getCasInfo(int);
     method public android.media.DrmInitData getDrmInitData();
@@ -23482,21 +23502,21 @@
     method public long getSampleSize();
     method public long getSampleTime();
     method public int getSampleTrackIndex();
-    method public final int getTrackCount();
+    method public int getTrackCount();
     method public android.media.MediaFormat getTrackFormat(int);
     method public boolean hasCacheReachedEndOfStream();
     method public int readSampleData(java.nio.ByteBuffer, int);
-    method public final void release();
+    method public void release();
     method public void seekTo(long, int);
     method public void selectTrack(int);
-    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
-    method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
-    method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
-    method public final void setDataSource(java.lang.String) throws java.io.IOException;
-    method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
-    method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
-    method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
-    method public final void setMediaCas(android.media.MediaCas);
+    method public void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
+    method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+    method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+    method public void setDataSource(java.lang.String) throws java.io.IOException;
+    method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
+    method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+    method public void setMediaCas(android.media.MediaCas);
     method public void unselectTrack(int);
     field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
     field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
@@ -23519,22 +23539,22 @@
 
   public final class MediaFormat {
     ctor public MediaFormat();
-    method public final boolean containsKey(java.lang.String);
-    method public static final android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
-    method public static final android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
-    method public static final android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
-    method public final java.nio.ByteBuffer getByteBuffer(java.lang.String);
+    method public boolean containsKey(java.lang.String);
+    method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
+    method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
+    method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
+    method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
     method public boolean getFeatureEnabled(java.lang.String);
-    method public final float getFloat(java.lang.String);
-    method public final int getInteger(java.lang.String);
-    method public final long getLong(java.lang.String);
-    method public final java.lang.String getString(java.lang.String);
-    method public final void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
+    method public float getFloat(java.lang.String);
+    method public int getInteger(java.lang.String);
+    method public long getLong(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
     method public void setFeatureEnabled(java.lang.String, boolean);
-    method public final void setFloat(java.lang.String, float);
-    method public final void setInteger(java.lang.String, int);
-    method public final void setLong(java.lang.String, long);
-    method public final void setString(java.lang.String, java.lang.String);
+    method public void setFloat(java.lang.String, float);
+    method public void setInteger(java.lang.String, int);
+    method public void setLong(java.lang.String, long);
+    method public void setString(java.lang.String, java.lang.String);
     field public static final int COLOR_RANGE_FULL = 1; // 0x1
     field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
     field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
@@ -24160,6 +24180,7 @@
     ctor public MediaRecorder();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
+    method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
     method public static final int getAudioSourceMax();
     method public int getMaxAmplitude() throws java.lang.IllegalStateException;
     method public android.os.PersistableBundle getMetrics();
@@ -24433,14 +24454,14 @@
 
   public final class MediaSync {
     ctor public MediaSync();
-    method public final android.view.Surface createInputSurface();
+    method public android.view.Surface createInputSurface();
     method protected void finalize();
     method public void flush();
     method public android.media.PlaybackParams getPlaybackParams();
     method public android.media.SyncParams getSyncParams();
     method public android.media.MediaTimestamp getTimestamp();
     method public void queueAudio(java.nio.ByteBuffer, int, long);
-    method public final void release();
+    method public void release();
     method public void setAudioTrack(android.media.AudioTrack);
     method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
     method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
@@ -24475,6 +24496,41 @@
     method public float getMediaClockRate();
   }
 
+  public final class MicrophoneInfo {
+    method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping();
+    method public java.lang.String getDescription();
+    method public int getDirectionality();
+    method public java.util.List<android.util.Pair<java.lang.Float, java.lang.Float>> getFrequencyResponse();
+    method public int getGroup();
+    method public int getId();
+    method public int getIndexInTheGroup();
+    method public int getLocation();
+    method public float getMaxSpl();
+    method public float getMinSpl();
+    method public android.media.MicrophoneInfo.Coordinate3F getOrientation();
+    method public android.media.MicrophoneInfo.Coordinate3F getPosition();
+    method public float getSensitivity();
+    method public int getType();
+    field public static final int CHANNEL_MAPPING_DIRECT = 1; // 0x1
+    field public static final int CHANNEL_MAPPING_PROCESSED = 2; // 0x2
+    field public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2; // 0x2
+    field public static final int DIRECTIONALITY_CARDIOID = 3; // 0x3
+    field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4
+    field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
+    field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
+    field public static final int DIRECTIONALITY_UNKNOW = 0; // 0x0
+    field public static final int LOCATION_MAINBODY = 1; // 0x1
+    field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
+    field public static final int LOCATION_PERIPHERAL = 3; // 0x3
+    field public static final int LOCATION_UNKNOWN = 0; // 0x0
+  }
+
+  public class MicrophoneInfo.Coordinate3F {
+    field public final float x;
+    field public final float y;
+    field public final float z;
+  }
+
   public final class NotProvisionedException extends android.media.MediaDrmException {
     ctor public NotProvisionedException(java.lang.String);
   }
@@ -25395,7 +25451,7 @@
 
   public final class MidiInputPort extends android.media.midi.MidiReceiver implements java.io.Closeable {
     method public void close() throws java.io.IOException;
-    method public final int getPortNumber();
+    method public int getPortNumber();
     method public void onSend(byte[], int, int, long) throws java.io.IOException;
   }
 
@@ -25420,7 +25476,7 @@
 
   public final class MidiOutputPort extends android.media.midi.MidiSender implements java.io.Closeable {
     method public void close() throws java.io.IOException;
-    method public final int getPortNumber();
+    method public int getPortNumber();
     method public void onConnect(android.media.midi.MidiReceiver);
     method public void onDisconnect(android.media.midi.MidiReceiver);
   }
@@ -25695,7 +25751,7 @@
 package android.media.tv {
 
   public final class TvContentRating {
-    method public final boolean contains(android.media.tv.TvContentRating);
+    method public boolean contains(android.media.tv.TvContentRating);
     method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
     method public java.lang.String flattenToString();
     method public java.lang.String getDomain();
@@ -25745,7 +25801,7 @@
   }
 
   public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
-    method public static final java.lang.String getVideoResolution(java.lang.String);
+    method public static java.lang.String getVideoResolution(java.lang.String);
     field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
     field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
     field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
@@ -26270,18 +26326,18 @@
 
   public final class TvTrackInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public final int getAudioChannelCount();
-    method public final int getAudioSampleRate();
-    method public final java.lang.CharSequence getDescription();
-    method public final android.os.Bundle getExtra();
-    method public final java.lang.String getId();
-    method public final java.lang.String getLanguage();
-    method public final int getType();
-    method public final byte getVideoActiveFormatDescription();
-    method public final float getVideoFrameRate();
-    method public final int getVideoHeight();
-    method public final float getVideoPixelAspectRatio();
-    method public final int getVideoWidth();
+    method public int getAudioChannelCount();
+    method public int getAudioSampleRate();
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtra();
+    method public java.lang.String getId();
+    method public java.lang.String getLanguage();
+    method public int getType();
+    method public byte getVideoActiveFormatDescription();
+    method public float getVideoFrameRate();
+    method public int getVideoHeight();
+    method public float getVideoPixelAspectRatio();
+    method public int getVideoWidth();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
     field public static final int TYPE_AUDIO = 0; // 0x0
@@ -26292,16 +26348,16 @@
   public static final class TvTrackInfo.Builder {
     ctor public TvTrackInfo.Builder(int, java.lang.String);
     method public android.media.tv.TvTrackInfo build();
-    method public final android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
-    method public final android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
-    method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
-    method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
-    method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
-    method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
-    method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
-    method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
-    method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
-    method public final android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
+    method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+    method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
+    method public android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
+    method public android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+    method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
+    method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
+    method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
+    method public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
+    method public android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
   }
 
   public class TvView extends android.view.ViewGroup {
@@ -26532,34 +26588,34 @@
   }
 
   public final class MtpObjectInfo {
-    method public final int getAssociationDesc();
-    method public final int getAssociationType();
-    method public final int getCompressedSize();
-    method public final long getCompressedSizeLong();
-    method public final long getDateCreated();
-    method public final long getDateModified();
-    method public final int getFormat();
-    method public final int getImagePixDepth();
-    method public final long getImagePixDepthLong();
-    method public final int getImagePixHeight();
-    method public final long getImagePixHeightLong();
-    method public final int getImagePixWidth();
-    method public final long getImagePixWidthLong();
-    method public final java.lang.String getKeywords();
-    method public final java.lang.String getName();
-    method public final int getObjectHandle();
-    method public final int getParent();
-    method public final int getProtectionStatus();
-    method public final int getSequenceNumber();
-    method public final long getSequenceNumberLong();
-    method public final int getStorageId();
-    method public final int getThumbCompressedSize();
-    method public final long getThumbCompressedSizeLong();
-    method public final int getThumbFormat();
-    method public final int getThumbPixHeight();
-    method public final long getThumbPixHeightLong();
-    method public final int getThumbPixWidth();
-    method public final long getThumbPixWidthLong();
+    method public int getAssociationDesc();
+    method public int getAssociationType();
+    method public int getCompressedSize();
+    method public long getCompressedSizeLong();
+    method public long getDateCreated();
+    method public long getDateModified();
+    method public int getFormat();
+    method public int getImagePixDepth();
+    method public long getImagePixDepthLong();
+    method public int getImagePixHeight();
+    method public long getImagePixHeightLong();
+    method public int getImagePixWidth();
+    method public long getImagePixWidthLong();
+    method public java.lang.String getKeywords();
+    method public java.lang.String getName();
+    method public int getObjectHandle();
+    method public int getParent();
+    method public int getProtectionStatus();
+    method public int getSequenceNumber();
+    method public long getSequenceNumberLong();
+    method public int getStorageId();
+    method public int getThumbCompressedSize();
+    method public long getThumbCompressedSizeLong();
+    method public int getThumbFormat();
+    method public int getThumbPixHeight();
+    method public long getThumbPixHeightLong();
+    method public int getThumbPixWidth();
+    method public long getThumbPixWidthLong();
   }
 
   public static class MtpObjectInfo.Builder {
@@ -26589,11 +26645,11 @@
   }
 
   public final class MtpStorageInfo {
-    method public final java.lang.String getDescription();
-    method public final long getFreeSpace();
-    method public final long getMaxCapacity();
-    method public final int getStorageId();
-    method public final java.lang.String getVolumeIdentifier();
+    method public java.lang.String getDescription();
+    method public long getFreeSpace();
+    method public long getMaxCapacity();
+    method public int getStorageId();
+    method public java.lang.String getVolumeIdentifier();
   }
 
 }
@@ -27026,10 +27082,10 @@
 
   public final class Proxy {
     ctor public Proxy();
-    method public static final deprecated java.lang.String getDefaultHost();
-    method public static final deprecated int getDefaultPort();
-    method public static final deprecated java.lang.String getHost(android.content.Context);
-    method public static final deprecated int getPort(android.content.Context);
+    method public static deprecated java.lang.String getDefaultHost();
+    method public static deprecated int getDefaultPort();
+    method public static deprecated java.lang.String getHost(android.content.Context);
+    method public static deprecated int getPort(android.content.Context);
     field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
     field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
   }
@@ -32056,9 +32112,9 @@
     method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
     method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
     method public static void enableEmulatorTraceOutput();
-    method public static final int getBinderDeathObjectCount();
-    method public static final int getBinderLocalObjectCount();
-    method public static final int getBinderProxyObjectCount();
+    method public static int getBinderDeathObjectCount();
+    method public static int getBinderLocalObjectCount();
+    method public static int getBinderProxyObjectCount();
     method public static int getBinderReceivedTransactions();
     method public static int getBinderSentTransactions();
     method public static deprecated int getGlobalAllocCount();
@@ -32467,114 +32523,114 @@
   }
 
   public final class Parcel {
-    method public final void appendFrom(android.os.Parcel, int, int);
-    method public final android.os.IBinder[] createBinderArray();
-    method public final java.util.ArrayList<android.os.IBinder> createBinderArrayList();
-    method public final boolean[] createBooleanArray();
-    method public final byte[] createByteArray();
-    method public final char[] createCharArray();
-    method public final double[] createDoubleArray();
-    method public final float[] createFloatArray();
-    method public final int[] createIntArray();
-    method public final long[] createLongArray();
-    method public final java.lang.String[] createStringArray();
-    method public final java.util.ArrayList<java.lang.String> createStringArrayList();
-    method public final <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
-    method public final <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
-    method public final int dataAvail();
-    method public final int dataCapacity();
-    method public final int dataPosition();
-    method public final int dataSize();
-    method public final void enforceInterface(java.lang.String);
-    method public final boolean hasFileDescriptors();
-    method public final byte[] marshall();
+    method public void appendFrom(android.os.Parcel, int, int);
+    method public android.os.IBinder[] createBinderArray();
+    method public java.util.ArrayList<android.os.IBinder> createBinderArrayList();
+    method public boolean[] createBooleanArray();
+    method public byte[] createByteArray();
+    method public char[] createCharArray();
+    method public double[] createDoubleArray();
+    method public float[] createFloatArray();
+    method public int[] createIntArray();
+    method public long[] createLongArray();
+    method public java.lang.String[] createStringArray();
+    method public java.util.ArrayList<java.lang.String> createStringArrayList();
+    method public <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
+    method public <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
+    method public int dataAvail();
+    method public int dataCapacity();
+    method public int dataPosition();
+    method public int dataSize();
+    method public void enforceInterface(java.lang.String);
+    method public boolean hasFileDescriptors();
+    method public byte[] marshall();
     method public static android.os.Parcel obtain();
-    method public final java.lang.Object[] readArray(java.lang.ClassLoader);
-    method public final java.util.ArrayList readArrayList(java.lang.ClassLoader);
-    method public final void readBinderArray(android.os.IBinder[]);
-    method public final void readBinderList(java.util.List<android.os.IBinder>);
-    method public final void readBooleanArray(boolean[]);
-    method public final android.os.Bundle readBundle();
-    method public final android.os.Bundle readBundle(java.lang.ClassLoader);
-    method public final byte readByte();
-    method public final void readByteArray(byte[]);
-    method public final void readCharArray(char[]);
-    method public final double readDouble();
-    method public final void readDoubleArray(double[]);
-    method public final void readException();
-    method public final void readException(int, java.lang.String);
-    method public final android.os.ParcelFileDescriptor readFileDescriptor();
-    method public final float readFloat();
-    method public final void readFloatArray(float[]);
-    method public final java.util.HashMap readHashMap(java.lang.ClassLoader);
-    method public final int readInt();
-    method public final void readIntArray(int[]);
-    method public final void readList(java.util.List, java.lang.ClassLoader);
-    method public final long readLong();
-    method public final void readLongArray(long[]);
-    method public final void readMap(java.util.Map, java.lang.ClassLoader);
-    method public final <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
-    method public final android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
-    method public final android.os.PersistableBundle readPersistableBundle();
-    method public final android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
-    method public final java.io.Serializable readSerializable();
-    method public final android.util.Size readSize();
-    method public final android.util.SizeF readSizeF();
-    method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
-    method public final android.util.SparseBooleanArray readSparseBooleanArray();
-    method public final java.lang.String readString();
-    method public final void readStringArray(java.lang.String[]);
-    method public final void readStringList(java.util.List<java.lang.String>);
-    method public final android.os.IBinder readStrongBinder();
-    method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
-    method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
-    method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
-    method public final java.lang.Object readValue(java.lang.ClassLoader);
-    method public final void recycle();
-    method public final void setDataCapacity(int);
-    method public final void setDataPosition(int);
-    method public final void setDataSize(int);
-    method public final void unmarshall(byte[], int, int);
-    method public final void writeArray(java.lang.Object[]);
-    method public final void writeBinderArray(android.os.IBinder[]);
-    method public final void writeBinderList(java.util.List<android.os.IBinder>);
-    method public final void writeBooleanArray(boolean[]);
-    method public final void writeBundle(android.os.Bundle);
-    method public final void writeByte(byte);
-    method public final void writeByteArray(byte[]);
-    method public final void writeByteArray(byte[], int, int);
-    method public final void writeCharArray(char[]);
-    method public final void writeDouble(double);
-    method public final void writeDoubleArray(double[]);
-    method public final void writeException(java.lang.Exception);
-    method public final void writeFileDescriptor(java.io.FileDescriptor);
-    method public final void writeFloat(float);
-    method public final void writeFloatArray(float[]);
-    method public final void writeInt(int);
-    method public final void writeIntArray(int[]);
-    method public final void writeInterfaceToken(java.lang.String);
-    method public final void writeList(java.util.List);
-    method public final void writeLong(long);
-    method public final void writeLongArray(long[]);
-    method public final void writeMap(java.util.Map);
-    method public final void writeNoException();
-    method public final void writeParcelable(android.os.Parcelable, int);
-    method public final <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
-    method public final void writePersistableBundle(android.os.PersistableBundle);
-    method public final void writeSerializable(java.io.Serializable);
-    method public final void writeSize(android.util.Size);
-    method public final void writeSizeF(android.util.SizeF);
-    method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
-    method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
-    method public final void writeString(java.lang.String);
-    method public final void writeStringArray(java.lang.String[]);
-    method public final void writeStringList(java.util.List<java.lang.String>);
-    method public final void writeStrongBinder(android.os.IBinder);
-    method public final void writeStrongInterface(android.os.IInterface);
-    method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
-    method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
-    method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
-    method public final void writeValue(java.lang.Object);
+    method public java.lang.Object[] readArray(java.lang.ClassLoader);
+    method public java.util.ArrayList readArrayList(java.lang.ClassLoader);
+    method public void readBinderArray(android.os.IBinder[]);
+    method public void readBinderList(java.util.List<android.os.IBinder>);
+    method public void readBooleanArray(boolean[]);
+    method public android.os.Bundle readBundle();
+    method public android.os.Bundle readBundle(java.lang.ClassLoader);
+    method public byte readByte();
+    method public void readByteArray(byte[]);
+    method public void readCharArray(char[]);
+    method public double readDouble();
+    method public void readDoubleArray(double[]);
+    method public void readException();
+    method public void readException(int, java.lang.String);
+    method public android.os.ParcelFileDescriptor readFileDescriptor();
+    method public float readFloat();
+    method public void readFloatArray(float[]);
+    method public java.util.HashMap readHashMap(java.lang.ClassLoader);
+    method public int readInt();
+    method public void readIntArray(int[]);
+    method public void readList(java.util.List, java.lang.ClassLoader);
+    method public long readLong();
+    method public void readLongArray(long[]);
+    method public void readMap(java.util.Map, java.lang.ClassLoader);
+    method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
+    method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+    method public android.os.PersistableBundle readPersistableBundle();
+    method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
+    method public java.io.Serializable readSerializable();
+    method public android.util.Size readSize();
+    method public android.util.SizeF readSizeF();
+    method public android.util.SparseArray readSparseArray(java.lang.ClassLoader);
+    method public android.util.SparseBooleanArray readSparseBooleanArray();
+    method public java.lang.String readString();
+    method public void readStringArray(java.lang.String[]);
+    method public void readStringList(java.util.List<java.lang.String>);
+    method public android.os.IBinder readStrongBinder();
+    method public <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
+    method public <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+    method public <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+    method public java.lang.Object readValue(java.lang.ClassLoader);
+    method public void recycle();
+    method public void setDataCapacity(int);
+    method public void setDataPosition(int);
+    method public void setDataSize(int);
+    method public void unmarshall(byte[], int, int);
+    method public void writeArray(java.lang.Object[]);
+    method public void writeBinderArray(android.os.IBinder[]);
+    method public void writeBinderList(java.util.List<android.os.IBinder>);
+    method public void writeBooleanArray(boolean[]);
+    method public void writeBundle(android.os.Bundle);
+    method public void writeByte(byte);
+    method public void writeByteArray(byte[]);
+    method public void writeByteArray(byte[], int, int);
+    method public void writeCharArray(char[]);
+    method public void writeDouble(double);
+    method public void writeDoubleArray(double[]);
+    method public void writeException(java.lang.Exception);
+    method public void writeFileDescriptor(java.io.FileDescriptor);
+    method public void writeFloat(float);
+    method public void writeFloatArray(float[]);
+    method public void writeInt(int);
+    method public void writeIntArray(int[]);
+    method public void writeInterfaceToken(java.lang.String);
+    method public void writeList(java.util.List);
+    method public void writeLong(long);
+    method public void writeLongArray(long[]);
+    method public void writeMap(java.util.Map);
+    method public void writeNoException();
+    method public void writeParcelable(android.os.Parcelable, int);
+    method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
+    method public void writePersistableBundle(android.os.PersistableBundle);
+    method public void writeSerializable(java.io.Serializable);
+    method public void writeSize(android.util.Size);
+    method public void writeSizeF(android.util.SizeF);
+    method public void writeSparseArray(android.util.SparseArray<java.lang.Object>);
+    method public void writeSparseBooleanArray(android.util.SparseBooleanArray);
+    method public void writeString(java.lang.String);
+    method public void writeStringArray(java.lang.String[]);
+    method public void writeStringList(java.util.List<java.lang.String>);
+    method public void writeStrongBinder(android.os.IBinder);
+    method public void writeStrongInterface(android.os.IInterface);
+    method public <T extends android.os.Parcelable> void writeTypedArray(T[], int);
+    method public <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
+    method public <T extends android.os.Parcelable> void writeTypedObject(T, int);
+    method public void writeValue(java.lang.Object);
     field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
   }
 
@@ -34228,7 +34284,7 @@
   }
 
   public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns {
-    method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+    method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
     field public static final android.net.Uri CONTENT_URI;
   }
 
@@ -34357,7 +34413,7 @@
   }
 
   public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns {
-    method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
+    method public static android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
     field public static final android.net.Uri CONTENT_URI;
   }
 
@@ -34450,8 +34506,8 @@
   }
 
   public static final class CalendarContract.Instances implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns {
-    method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
-    method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
+    method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
+    method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
     field public static final java.lang.String BEGIN = "begin";
     field public static final android.net.Uri CONTENT_BY_DAY_URI;
     field public static final android.net.Uri CONTENT_SEARCH_BY_DAY_URI;
@@ -34466,7 +34522,7 @@
   }
 
   public static final class CalendarContract.Reminders implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.RemindersColumns {
-    method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+    method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
     field public static final android.net.Uri CONTENT_URI;
   }
 
@@ -34575,7 +34631,7 @@
     method public static deprecated java.lang.Object decodeImProtocol(java.lang.String);
     method public static deprecated java.lang.String encodeCustomImProtocol(java.lang.String);
     method public static deprecated java.lang.String encodePredefinedImProtocol(int);
-    method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
+    method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
     field public static final deprecated java.lang.String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email";
     field public static final deprecated java.lang.String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email";
     field public static final deprecated android.net.Uri CONTENT_EMAIL_URI;
@@ -34725,7 +34781,7 @@
   }
 
   public static final deprecated class Contacts.Organizations implements android.provider.BaseColumns android.provider.Contacts.OrganizationColumns {
-    method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+    method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
     field public static final deprecated java.lang.String CONTENT_DIRECTORY = "organizations";
     field public static final deprecated android.net.Uri CONTENT_URI;
     field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "company, title, isprimary ASC";
@@ -34782,8 +34838,8 @@
   }
 
   public static final deprecated class Contacts.Phones implements android.provider.BaseColumns android.provider.Contacts.PeopleColumns android.provider.Contacts.PhonesColumns {
-    method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
-    method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+    method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
+    method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
     field public static final deprecated android.net.Uri CONTENT_FILTER_URL;
     field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone";
     field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone";
@@ -34923,8 +34979,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String ADDRESS = "data1";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2";
@@ -34944,7 +35000,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static int getTypeResource(java.lang.Integer);
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -34975,10 +35031,10 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getProtocolLabelResource(int);
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getProtocolLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
     field public static final java.lang.String CUSTOM_PROTOCOL = "data6";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35023,8 +35079,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String COMPANY = "data1";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
     field public static final java.lang.String DEPARTMENT = "data5";
@@ -35042,8 +35098,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2";
@@ -35088,8 +35144,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -35112,8 +35168,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -35143,8 +35199,8 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
-    method public static final int getTypeLabelResource(int);
+    method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+    method public static int getTypeLabelResource(int);
     field public static final java.lang.String CITY = "data7";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2";
@@ -35971,7 +36027,7 @@
 
   public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
     ctor public MediaStore.Audio.Artists.Albums();
-    method public static final android.net.Uri getContentUri(java.lang.String, long);
+    method public static android.net.Uri getContentUri(java.lang.String, long);
   }
 
   public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns {
@@ -36007,7 +36063,7 @@
 
   public static final class MediaStore.Audio.Genres.Members implements android.provider.MediaStore.Audio.AudioColumns {
     ctor public MediaStore.Audio.Genres.Members();
-    method public static final android.net.Uri getContentUri(java.lang.String, long);
+    method public static android.net.Uri getContentUri(java.lang.String, long);
     field public static final java.lang.String AUDIO_ID = "audio_id";
     field public static final java.lang.String CONTENT_DIRECTORY = "members";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "title_key";
@@ -36043,8 +36099,8 @@
 
   public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns {
     ctor public MediaStore.Audio.Playlists.Members();
-    method public static final android.net.Uri getContentUri(java.lang.String, long);
-    method public static final boolean moveItem(android.content.ContentResolver, long, int, int);
+    method public static android.net.Uri getContentUri(java.lang.String, long);
+    method public static boolean moveItem(android.content.ContentResolver, long, int, int);
     field public static final java.lang.String AUDIO_ID = "audio_id";
     field public static final java.lang.String CONTENT_DIRECTORY = "members";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "play_order";
@@ -36067,7 +36123,7 @@
   public static final class MediaStore.Files {
     ctor public MediaStore.Files();
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static final android.net.Uri getContentUri(java.lang.String, long);
+    method public static android.net.Uri getContentUri(java.lang.String, long);
   }
 
   public static abstract interface MediaStore.Files.FileColumns implements android.provider.MediaStore.MediaColumns {
@@ -36101,13 +36157,13 @@
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
     ctor public MediaStore.Images.Media();
-    method public static final android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
+    method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static final java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
-    method public static final java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
-    method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
-    method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
-    method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public static java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public static java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/image";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "bucket_display_name";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
@@ -36152,7 +36208,7 @@
 
   public static final class MediaStore.Video {
     ctor public MediaStore.Video();
-    method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+    method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
     field public static final java.lang.String DEFAULT_SORT_ORDER = "_display_name";
   }
 
@@ -36384,12 +36440,12 @@
     method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
     method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
     method public static android.net.Uri getUriFor(java.lang.String);
-    method public static final deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
+    method public static deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
     method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
     method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
     method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
-    method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
+    method public static deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
     field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
     field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
@@ -38192,6 +38248,37 @@
     method public java.security.KeyPair getKeyPair();
   }
 
+  public class ConfirmationAlreadyPresentingException extends java.lang.Exception {
+    ctor public ConfirmationAlreadyPresentingException();
+    ctor public ConfirmationAlreadyPresentingException(java.lang.String);
+  }
+
+  public abstract class ConfirmationCallback {
+    ctor public ConfirmationCallback();
+    method public void onConfirmedByUser(byte[]);
+    method public void onDismissedByApplication();
+    method public void onDismissedByUser();
+    method public void onError(java.lang.Exception);
+  }
+
+  public class ConfirmationDialog {
+    method public void cancelPrompt();
+    method public static boolean isSupported();
+    method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException;
+  }
+
+  public static class ConfirmationDialog.Builder {
+    ctor public ConfirmationDialog.Builder();
+    method public android.security.ConfirmationDialog build(android.content.Context);
+    method public android.security.ConfirmationDialog.Builder setExtraData(byte[]);
+    method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence);
+  }
+
+  public class ConfirmationNotAvailableException extends java.lang.Exception {
+    ctor public ConfirmationNotAvailableException();
+    ctor public ConfirmationNotAvailableException(java.lang.String);
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -38301,6 +38388,7 @@
     method public boolean isTrustedUserPresenceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public static final class KeyGenParameterSpec.Builder {
@@ -38328,6 +38416,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean);
   }
 
   public class KeyInfo implements java.security.spec.KeySpec {
@@ -38349,6 +38438,7 @@
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -38416,6 +38506,7 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public static final class KeyProtection.Builder {
@@ -38434,6 +38525,7 @@
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
+    method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean);
   }
 
   public class StrongBoxUnavailableException extends java.security.ProviderException {
@@ -38531,6 +38623,20 @@
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
+  public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
+    ctor public DateTransformation(android.view.autofill.AutofillId, java.text.DateFormat);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.DateTransformation> CREATOR;
+  }
+
+  public final class DateValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public DateValueSanitizer(java.text.DateFormat);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.DateValueSanitizer> CREATOR;
+  }
+
   public final class FieldClassification {
     method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
   }
@@ -38700,6 +38806,7 @@
   public final class UserData implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String getFieldClassificationAlgorithm();
+    method public java.lang.String getId();
     method public static int getMaxFieldClassificationIdsSize();
     method public static int getMaxUserDataSize();
     method public static int getMaxValueLength();
@@ -38709,7 +38816,7 @@
   }
 
   public static final class UserData.Builder {
-    ctor public UserData.Builder(java.lang.String, java.lang.String);
+    ctor public UserData.Builder(java.lang.String, java.lang.String, java.lang.String);
     method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
     method public android.service.autofill.UserData build();
     method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle);
@@ -40370,12 +40477,12 @@
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void pullExternalCall();
-    method public final void putExtras(android.os.Bundle);
+    method public void putExtras(android.os.Bundle);
     method public void registerCallback(android.telecom.Call.Callback);
     method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
     method public void reject(boolean, java.lang.String);
-    method public final void removeExtras(java.util.List<java.lang.String>);
-    method public final void removeExtras(java.lang.String...);
+    method public void removeExtras(java.util.List<java.lang.String>);
+    method public void removeExtras(java.lang.String...);
     method public void respondToRttRequest(int, boolean);
     method public void sendCallEvent(java.lang.String, android.os.Bundle);
     method public void sendRttRequest();
@@ -40944,23 +41051,23 @@
   public final class RemoteConference {
     method public void disconnect();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
-    method public final int getConnectionCapabilities();
-    method public final int getConnectionProperties();
-    method public final java.util.List<android.telecom.RemoteConnection> getConnections();
+    method public int getConnectionCapabilities();
+    method public int getConnectionProperties();
+    method public java.util.List<android.telecom.RemoteConnection> getConnections();
     method public android.telecom.DisconnectCause getDisconnectCause();
-    method public final android.os.Bundle getExtras();
-    method public final int getState();
+    method public android.os.Bundle getExtras();
+    method public int getState();
     method public void hold();
     method public void merge();
     method public void playDtmfTone(char);
-    method public final void registerCallback(android.telecom.RemoteConference.Callback);
-    method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
+    method public void registerCallback(android.telecom.RemoteConference.Callback);
+    method public void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
     method public void separate(android.telecom.RemoteConnection);
     method public void setCallAudioState(android.telecom.CallAudioState);
     method public void stopDtmfTone();
     method public void swap();
     method public void unhold();
-    method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
+    method public void unregisterCallback(android.telecom.RemoteConference.Callback);
   }
 
   public static abstract class RemoteConference.Callback {
@@ -40989,10 +41096,10 @@
     method public int getConnectionCapabilities();
     method public int getConnectionProperties();
     method public android.telecom.DisconnectCause getDisconnectCause();
-    method public final android.os.Bundle getExtras();
+    method public android.os.Bundle getExtras();
     method public int getState();
     method public android.telecom.StatusHints getStatusHints();
-    method public final android.telecom.RemoteConnection.VideoProvider getVideoProvider();
+    method public android.telecom.RemoteConnection.VideoProvider getVideoProvider();
     method public int getVideoState();
     method public void hold();
     method public boolean isRingbackRequested();
@@ -42445,11 +42552,11 @@
   }
 
   public final deprecated class SmsManager {
-    method public final deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
-    method public static final deprecated android.telephony.gsm.SmsManager getDefault();
-    method public final deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
-    method public final deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
-    method public final deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+    method public deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
+    method public static deprecated android.telephony.gsm.SmsManager getDefault();
+    method public deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
+    method public deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
+    method public deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
     field public static final deprecated int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
     field public static final deprecated int RESULT_ERROR_NO_SERVICE = 4; // 0x4
     field public static final deprecated int RESULT_ERROR_NULL_PDU = 3; // 0x3
@@ -44321,6 +44428,11 @@
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static java.util.concurrent.Future<java.lang.Void> addLinksAsync(android.widget.TextView, android.view.textclassifier.TextLinks.Options);
+    method public static java.util.concurrent.Future<java.lang.Void> addLinksAsync(android.widget.TextView, android.view.textclassifier.TextLinks.Options, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>);
+    method public static java.util.concurrent.Future<java.lang.Void> addLinksAsync(android.text.Spannable, android.view.textclassifier.TextClassifier, android.view.textclassifier.TextLinks.Options);
+    method public static java.util.concurrent.Future<java.lang.Void> addLinksAsync(android.text.Spannable, android.view.textclassifier.TextClassifier, int);
+    method public static java.util.concurrent.Future<java.lang.Void> addLinksAsync(android.text.Spannable, android.view.textclassifier.TextClassifier, android.view.textclassifier.TextLinks.Options, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>);
     field public static final int ALL = 15; // 0xf
     field public static final int EMAIL_ADDRESSES = 2; // 0x2
     field public static final int MAP_ADDRESSES = 8; // 0x8
@@ -46503,76 +46615,76 @@
 
   public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
     method public static java.lang.String actionToString(int);
-    method public final void addBatch(long, float, float, float, float, int);
-    method public final void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
+    method public void addBatch(long, float, float, float, float, int);
+    method public void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
     method public static int axisFromString(java.lang.String);
     method public static java.lang.String axisToString(int);
-    method public final int findPointerIndex(int);
-    method public final int getAction();
-    method public final int getActionButton();
-    method public final int getActionIndex();
-    method public final int getActionMasked();
-    method public final float getAxisValue(int);
-    method public final float getAxisValue(int, int);
-    method public final int getButtonState();
-    method public final int getDeviceId();
-    method public final long getDownTime();
-    method public final int getEdgeFlags();
-    method public final long getEventTime();
-    method public final int getFlags();
-    method public final float getHistoricalAxisValue(int, int);
-    method public final float getHistoricalAxisValue(int, int, int);
-    method public final long getHistoricalEventTime(int);
-    method public final float getHistoricalOrientation(int);
-    method public final float getHistoricalOrientation(int, int);
-    method public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
-    method public final float getHistoricalPressure(int);
-    method public final float getHistoricalPressure(int, int);
-    method public final float getHistoricalSize(int);
-    method public final float getHistoricalSize(int, int);
-    method public final float getHistoricalToolMajor(int);
-    method public final float getHistoricalToolMajor(int, int);
-    method public final float getHistoricalToolMinor(int);
-    method public final float getHistoricalToolMinor(int, int);
-    method public final float getHistoricalTouchMajor(int);
-    method public final float getHistoricalTouchMajor(int, int);
-    method public final float getHistoricalTouchMinor(int);
-    method public final float getHistoricalTouchMinor(int, int);
-    method public final float getHistoricalX(int);
-    method public final float getHistoricalX(int, int);
-    method public final float getHistoricalY(int);
-    method public final float getHistoricalY(int, int);
-    method public final int getHistorySize();
-    method public final int getMetaState();
-    method public final float getOrientation();
-    method public final float getOrientation(int);
-    method public final void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
-    method public final int getPointerCount();
-    method public final int getPointerId(int);
-    method public final void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
-    method public final float getPressure();
-    method public final float getPressure(int);
-    method public final float getRawX();
-    method public final float getRawY();
-    method public final float getSize();
-    method public final float getSize(int);
-    method public final int getSource();
-    method public final float getToolMajor();
-    method public final float getToolMajor(int);
-    method public final float getToolMinor();
-    method public final float getToolMinor(int);
-    method public final int getToolType(int);
-    method public final float getTouchMajor();
-    method public final float getTouchMajor(int);
-    method public final float getTouchMinor();
-    method public final float getTouchMinor(int);
-    method public final float getX();
-    method public final float getX(int);
-    method public final float getXPrecision();
-    method public final float getY();
-    method public final float getY(int);
-    method public final float getYPrecision();
-    method public final boolean isButtonPressed(int);
+    method public int findPointerIndex(int);
+    method public int getAction();
+    method public int getActionButton();
+    method public int getActionIndex();
+    method public int getActionMasked();
+    method public float getAxisValue(int);
+    method public float getAxisValue(int, int);
+    method public int getButtonState();
+    method public int getDeviceId();
+    method public long getDownTime();
+    method public int getEdgeFlags();
+    method public long getEventTime();
+    method public int getFlags();
+    method public float getHistoricalAxisValue(int, int);
+    method public float getHistoricalAxisValue(int, int, int);
+    method public long getHistoricalEventTime(int);
+    method public float getHistoricalOrientation(int);
+    method public float getHistoricalOrientation(int, int);
+    method public void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
+    method public float getHistoricalPressure(int);
+    method public float getHistoricalPressure(int, int);
+    method public float getHistoricalSize(int);
+    method public float getHistoricalSize(int, int);
+    method public float getHistoricalToolMajor(int);
+    method public float getHistoricalToolMajor(int, int);
+    method public float getHistoricalToolMinor(int);
+    method public float getHistoricalToolMinor(int, int);
+    method public float getHistoricalTouchMajor(int);
+    method public float getHistoricalTouchMajor(int, int);
+    method public float getHistoricalTouchMinor(int);
+    method public float getHistoricalTouchMinor(int, int);
+    method public float getHistoricalX(int);
+    method public float getHistoricalX(int, int);
+    method public float getHistoricalY(int);
+    method public float getHistoricalY(int, int);
+    method public int getHistorySize();
+    method public int getMetaState();
+    method public float getOrientation();
+    method public float getOrientation(int);
+    method public void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
+    method public int getPointerCount();
+    method public int getPointerId(int);
+    method public void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
+    method public float getPressure();
+    method public float getPressure(int);
+    method public float getRawX();
+    method public float getRawY();
+    method public float getSize();
+    method public float getSize(int);
+    method public int getSource();
+    method public float getToolMajor();
+    method public float getToolMajor(int);
+    method public float getToolMinor();
+    method public float getToolMinor(int);
+    method public int getToolType(int);
+    method public float getTouchMajor();
+    method public float getTouchMajor(int);
+    method public float getTouchMinor();
+    method public float getTouchMinor(int);
+    method public float getX();
+    method public float getX(int);
+    method public float getXPrecision();
+    method public float getY();
+    method public float getY(int);
+    method public float getYPrecision();
+    method public boolean isButtonPressed(int);
     method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
     method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
     method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -46580,13 +46692,13 @@
     method public static android.view.MotionEvent obtain(long, long, int, float, float, int);
     method public static android.view.MotionEvent obtain(android.view.MotionEvent);
     method public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent);
-    method public final void offsetLocation(float, float);
-    method public final void recycle();
-    method public final void setAction(int);
-    method public final void setEdgeFlags(int);
-    method public final void setLocation(float, float);
-    method public final void setSource(int);
-    method public final void transform(android.graphics.Matrix);
+    method public void offsetLocation(float, float);
+    method public void recycle();
+    method public void setAction(int);
+    method public void setEdgeFlags(int);
+    method public void setLocation(float, float);
+    method public void setSource(int);
+    method public void transform(android.graphics.Matrix);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
     field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
@@ -47147,7 +47259,9 @@
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+    method public int getOutlineAmbientShadowColor();
     method public android.view.ViewOutlineProvider getOutlineProvider();
+    method public int getOutlineSpotShadowColor();
     method public int getOverScrollMode();
     method public android.view.ViewOverlay getOverlay();
     method public int getPaddingBottom();
@@ -47469,7 +47583,9 @@
     method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
     method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
     method public void setOnTouchListener(android.view.View.OnTouchListener);
+    method public void setOutlineAmbientShadowColor(int);
     method public void setOutlineProvider(android.view.ViewOutlineProvider);
+    method public void setOutlineSpotShadowColor(int);
     method public void setOverScrollMode(int);
     method public void setPadding(int, int, int, int);
     method public void setPaddingRelative(int, int, int, int);
@@ -48304,9 +48420,9 @@
     method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
     method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
     method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
-    method public final void dispatchOnDraw();
-    method public final void dispatchOnGlobalLayout();
-    method public final boolean dispatchOnPreDraw();
+    method public void dispatchOnDraw();
+    method public void dispatchOnGlobalLayout();
+    method public boolean dispatchOnPreDraw();
     method public boolean isAlive();
     method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
     method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
@@ -49561,6 +49677,7 @@
     method public java.util.List<java.lang.String> getAvailableFieldClassificationAlgorithms();
     method public java.lang.String getDefaultFieldClassificationAlgorithm();
     method public android.service.autofill.UserData getUserData();
+    method public java.lang.String getUserDataId();
     method public boolean hasEnabledAutofillServices();
     method public boolean isAutofillSupported();
     method public boolean isEnabled();
@@ -50016,7 +50133,8 @@
 
 package android.view.textclassifier {
 
-  public final class TextClassification {
+  public final class TextClassification implements android.os.Parcelable {
+    method public int describeContents();
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
@@ -50030,6 +50148,8 @@
     method public java.lang.CharSequence getSecondaryLabel(int);
     method public java.lang.String getSignature();
     method public java.lang.String getText();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification> CREATOR;
   }
 
   public static final class TextClassification.Builder {
@@ -50070,6 +50190,7 @@
     method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
     method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
     method public default java.util.Collection<java.lang.String> getEntitiesForPreset(int);
+    method public default android.view.textclassifier.logging.Logger getLogger(android.view.textclassifier.logging.Logger.Config);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
@@ -50099,32 +50220,42 @@
   }
 
   public final class TextLinks implements android.os.Parcelable {
-    method public boolean apply(android.text.SpannableString, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.text.style.ClickableSpan>);
     method public int describeContents();
     method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
+    field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks> CREATOR;
+    field public static final int STATUS_DIFFERENT_TEXT = 3; // 0x3
+    field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
+    field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
+    field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
   }
 
   public static final class TextLinks.Builder {
     ctor public TextLinks.Builder(java.lang.String);
-    method public android.view.textclassifier.TextLinks.Builder addLink(android.view.textclassifier.TextLinks.TextLink);
+    method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
     method public android.view.textclassifier.TextLinks build();
+    method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
   }
 
   public static final class TextLinks.Options implements android.os.Parcelable {
     ctor public TextLinks.Options();
     method public int describeContents();
+    method public static android.view.textclassifier.TextLinks.Options fromLinkMask(int);
+    method public int getApplyStrategy();
     method public android.os.LocaleList getDefaultLocales();
     method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
+    method public java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan> getSpanFactory();
+    method public android.view.textclassifier.TextLinks.Options setApplyStrategy(int);
     method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList);
     method public android.view.textclassifier.TextLinks.Options setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig);
+    method public android.view.textclassifier.TextLinks.Options setSpanFactory(java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.Options> CREATOR;
   }
 
   public static final class TextLinks.TextLink implements android.os.Parcelable {
-    ctor public TextLinks.TextLink(java.lang.String, int, int, java.util.Map<java.lang.String, java.lang.Float>);
     method public int describeContents();
     method public float getConfidenceScore(java.lang.String);
     method public int getEnd();
@@ -50135,13 +50266,22 @@
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
   }
 
-  public final class TextSelection {
+  public static class TextLinks.TextLinkSpan extends android.text.style.ClickableSpan {
+    ctor public TextLinks.TextLinkSpan(android.view.textclassifier.TextLinks.TextLink);
+    method public final android.view.textclassifier.TextLinks.TextLink getTextLink();
+    method public void onClick(android.view.View);
+  }
+
+  public final class TextSelection implements android.os.Parcelable {
+    method public int describeContents();
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
     method public int getSelectionEndIndex();
     method public int getSelectionStartIndex();
     method public java.lang.String getSignature();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection> CREATOR;
   }
 
   public static final class TextSelection.Builder {
@@ -50162,6 +50302,75 @@
 
 }
 
+package android.view.textclassifier.logging {
+
+  public abstract class Logger {
+    ctor public Logger(android.view.textclassifier.logging.Logger.Config);
+    method public java.text.BreakIterator getTokenIterator(java.util.Locale);
+    method public boolean isSmartSelection(java.lang.String);
+    method public final void logSelectionActionEvent(int, int, int);
+    method public final void logSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
+    method public final void logSelectionModifiedEvent(int, int);
+    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification);
+    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection);
+    method public final void logSelectionStartedEvent(int);
+    method public abstract void writeEvent(android.view.textclassifier.logging.SelectionEvent);
+    field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff
+    field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000
+    field public static final java.lang.String WIDGET_CUSTOM_EDITTEXT = "customedit";
+    field public static final java.lang.String WIDGET_CUSTOM_TEXTVIEW = "customview";
+    field public static final java.lang.String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
+    field public static final java.lang.String WIDGET_EDITTEXT = "edittext";
+    field public static final java.lang.String WIDGET_EDIT_WEBVIEW = "edit-webview";
+    field public static final java.lang.String WIDGET_TEXTVIEW = "textview";
+    field public static final java.lang.String WIDGET_UNKNOWN = "unknown";
+    field public static final java.lang.String WIDGET_UNSELECTABLE_TEXTVIEW = "nosel-textview";
+    field public static final java.lang.String WIDGET_WEBVIEW = "webview";
+  }
+
+  public static final class Logger.Config {
+    ctor public Logger.Config(android.content.Context, java.lang.String, java.lang.String);
+    method public java.lang.String getPackageName();
+    method public java.lang.String getWidgetType();
+    method public java.lang.String getWidgetVersion();
+  }
+
+  public final class SelectionEvent {
+    method public long getDurationSincePreviousEvent();
+    method public long getDurationSinceSessionStart();
+    method public int getEnd();
+    method public java.lang.String getEntityType();
+    method public int getEventIndex();
+    method public long getEventTime();
+    method public int getEventType();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getSessionId();
+    method public java.lang.String getSignature();
+    method public int getSmartEnd();
+    method public int getSmartStart();
+    method public int getStart();
+    method public java.lang.String getWidgetType();
+    method public java.lang.String getWidgetVersion();
+    field public static final int ACTION_ABANDON = 107; // 0x6b
+    field public static final int ACTION_COPY = 101; // 0x65
+    field public static final int ACTION_CUT = 103; // 0x67
+    field public static final int ACTION_DRAG = 106; // 0x6a
+    field public static final int ACTION_OTHER = 108; // 0x6c
+    field public static final int ACTION_OVERTYPE = 100; // 0x64
+    field public static final int ACTION_PASTE = 102; // 0x66
+    field public static final int ACTION_RESET = 201; // 0xc9
+    field public static final int ACTION_SELECT_ALL = 200; // 0xc8
+    field public static final int ACTION_SHARE = 104; // 0x68
+    field public static final int ACTION_SMART_SHARE = 105; // 0x69
+    field public static final int EVENT_AUTO_SELECTION = 5; // 0x5
+    field public static final int EVENT_SELECTION_MODIFIED = 2; // 0x2
+    field public static final int EVENT_SELECTION_STARTED = 1; // 0x1
+    field public static final int EVENT_SMART_SELECTION_MULTI = 4; // 0x4
+    field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3
+  }
+
+}
+
 package android.view.textservice {
 
   public final class SentenceSuggestionsInfo implements android.os.Parcelable {
@@ -50474,7 +50683,7 @@
     ctor public URLUtil();
     method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
     method public static byte[] decode(byte[]) throws java.lang.IllegalArgumentException;
-    method public static final java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
+    method public static java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
     method public static java.lang.String guessUrl(java.lang.String);
     method public static boolean isAboutUrl(java.lang.String);
     method public static boolean isAssetUrl(java.lang.String);
@@ -52286,6 +52495,7 @@
     ctor public Magnifier(android.view.View);
     method public void dismiss();
     method public void show(float, float);
+    method public void update();
   }
 
   public class MediaController extends android.widget.FrameLayout {
@@ -55542,7 +55752,7 @@
   }
 
   public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
-    method public static final java.lang.Character.UnicodeBlock forName(java.lang.String);
+    method public static java.lang.Character.UnicodeBlock forName(java.lang.String);
     method public static java.lang.Character.UnicodeBlock of(char);
     method public static java.lang.Character.UnicodeBlock of(int);
     field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
@@ -55769,7 +55979,7 @@
   }
 
   public static final class Character.UnicodeScript extends java.lang.Enum {
-    method public static final java.lang.Character.UnicodeScript forName(java.lang.String);
+    method public static java.lang.Character.UnicodeScript forName(java.lang.String);
     method public static java.lang.Character.UnicodeScript of(int);
     method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
     method public static final java.lang.Character.UnicodeScript[] values();
@@ -57432,6 +57642,7 @@
     method public boolean enqueue();
     method public T get();
     method public boolean isEnqueued();
+    method public static void reachabilityFence(java.lang.Object);
   }
 
   public class ReferenceQueue<T> {
@@ -58584,8 +58795,8 @@
     ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException;
     ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
     method public java.lang.String getAuthority();
-    method public final java.lang.Object getContent() throws java.io.IOException;
-    method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
+    method public java.lang.Object getContent() throws java.io.IOException;
+    method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
     method public int getDefaultPort();
     method public java.lang.String getFile();
     method public java.lang.String getHost();
@@ -58598,7 +58809,7 @@
     method public synchronized int hashCode();
     method public java.net.URLConnection openConnection() throws java.io.IOException;
     method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
-    method public final java.io.InputStream openStream() throws java.io.IOException;
+    method public java.io.InputStream openStream() throws java.io.IOException;
     method public boolean sameFile(java.net.URL);
     method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
     method public java.lang.String toExternalForm();
@@ -63254,13 +63465,13 @@
     method public int getOffset();
     method public int next();
     method public int previous();
-    method public static final int primaryOrder(int);
+    method public static int primaryOrder(int);
     method public void reset();
-    method public static final short secondaryOrder(int);
+    method public static short secondaryOrder(int);
     method public void setOffset(int);
     method public void setText(java.lang.String);
     method public void setText(java.text.CharacterIterator);
-    method public static final short tertiaryOrder(int);
+    method public static short tertiaryOrder(int);
     field public static final int NULLORDER = -1; // 0xffffffff
   }
 
@@ -64541,7 +64752,7 @@
   }
 
   public final class HijrahDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
-    method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
     method public static java.time.chrono.HijrahDate from(java.time.temporal.TemporalAccessor);
     method public java.time.chrono.HijrahChronology getChronology();
     method public java.time.chrono.HijrahEra getEra();
@@ -64628,7 +64839,7 @@
   }
 
   public final class JapaneseDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
-    method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
     method public static java.time.chrono.JapaneseDate from(java.time.temporal.TemporalAccessor);
     method public java.time.chrono.JapaneseChronology getChronology();
     method public java.time.chrono.JapaneseEra getEra();
@@ -64684,7 +64895,7 @@
   }
 
   public final class MinguoDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
-    method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
     method public static java.time.chrono.MinguoDate from(java.time.temporal.TemporalAccessor);
     method public java.time.chrono.MinguoChronology getChronology();
     method public java.time.chrono.MinguoEra getEra();
@@ -64737,7 +64948,7 @@
   }
 
   public final class ThaiBuddhistDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
-    method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
     method public static java.time.chrono.ThaiBuddhistDate from(java.time.temporal.TemporalAccessor);
     method public java.time.chrono.ThaiBuddhistChronology getChronology();
     method public java.time.chrono.ThaiBuddhistEra getEra();
@@ -64789,8 +65000,8 @@
     method public <T> T parse(java.lang.CharSequence, java.time.temporal.TemporalQuery<T>);
     method public java.time.temporal.TemporalAccessor parseBest(java.lang.CharSequence, java.time.temporal.TemporalQuery<?>...);
     method public java.time.temporal.TemporalAccessor parseUnresolved(java.lang.CharSequence, java.text.ParsePosition);
-    method public static final java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
-    method public static final java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
+    method public static java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
+    method public static java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
     method public java.text.Format toFormat();
     method public java.text.Format toFormat(java.time.temporal.TemporalQuery<?>);
     method public java.time.format.DateTimeFormatter withChronology(java.time.chrono.Chronology);
@@ -66239,15 +66450,15 @@
     method public java.lang.String getCountry();
     method public static java.util.Locale getDefault();
     method public static java.util.Locale getDefault(java.util.Locale.Category);
-    method public final java.lang.String getDisplayCountry();
+    method public java.lang.String getDisplayCountry();
     method public java.lang.String getDisplayCountry(java.util.Locale);
-    method public final java.lang.String getDisplayLanguage();
+    method public java.lang.String getDisplayLanguage();
     method public java.lang.String getDisplayLanguage(java.util.Locale);
-    method public final java.lang.String getDisplayName();
+    method public java.lang.String getDisplayName();
     method public java.lang.String getDisplayName(java.util.Locale);
     method public java.lang.String getDisplayScript();
     method public java.lang.String getDisplayScript(java.util.Locale);
-    method public final java.lang.String getDisplayVariant();
+    method public java.lang.String getDisplayVariant();
     method public java.lang.String getDisplayVariant(java.util.Locale);
     method public java.lang.String getExtension(char);
     method public java.util.Set<java.lang.Character> getExtensionKeys();
@@ -66268,7 +66479,6 @@
     method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
     method public java.util.Locale stripExtensions();
     method public java.lang.String toLanguageTag();
-    method public final java.lang.String toString();
     field public static final java.util.Locale CANADA;
     field public static final java.util.Locale CANADA_FRENCH;
     field public static final java.util.Locale CHINA;
@@ -70454,7 +70664,6 @@
 
   public class ExemptionMechanism {
     ctor protected ExemptionMechanism(javax.crypto.ExemptionMechanismSpi, java.security.Provider, java.lang.String);
-    method protected void finalize();
     method public final byte[] genExemptionBlob() throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException;
     method public final int genExemptionBlob(byte[]) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
     method public final int genExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 5da5864..ad71e7c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19,6 +19,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
     field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
+    field public static final java.lang.String BIND_DATA_SERVICE = "android.permission.BIND_DATA_SERVICE";
     field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
@@ -29,6 +30,7 @@
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+    field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
     field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
     field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
     field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
@@ -821,8 +823,16 @@
     field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
     field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
     field public static final java.lang.String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
+    field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
     field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
+    field public static final java.lang.String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
     field public static final java.lang.String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
+    field public static final java.lang.String EXTRA_INSTANT_APP_EXTRAS = "android.intent.extra.INSTANT_APP_EXTRAS";
+    field public static final java.lang.String EXTRA_INSTANT_APP_FAILURE = "android.intent.extra.INSTANT_APP_FAILURE";
+    field public static final java.lang.String EXTRA_INSTANT_APP_HOSTNAME = "android.intent.extra.INSTANT_APP_HOSTNAME";
+    field public static final java.lang.String EXTRA_INSTANT_APP_SUCCESS = "android.intent.extra.INSTANT_APP_SUCCESS";
+    field public static final java.lang.String EXTRA_INSTANT_APP_TOKEN = "android.intent.extra.INSTANT_APP_TOKEN";
+    field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
     field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
     field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
     field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
@@ -830,6 +840,7 @@
     field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
+    field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
   }
 
   public class IntentFilter implements android.os.Parcelable {
@@ -842,7 +853,9 @@
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    method public boolean isInstantApp();
     field public java.lang.String credentialProtectedDataDir;
+    field public int targetSandboxVersion;
   }
 
   public final class InstantAppInfo implements android.os.Parcelable {
@@ -1128,6 +1141,15 @@
 
 package android.hardware.display {
 
+  public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+    method public int describeContents();
+    method public float[] getBucketBoundaries();
+    method public java.time.LocalDate getLocalDate();
+    method public float[] getStats();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+  }
+
   public final class BrightnessChangeEvent implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -1135,11 +1157,14 @@
     field public final float batteryLevel;
     field public final float brightness;
     field public final int colorTemperature;
+    field public final boolean isDefaultBrightnessConfig;
+    field public final boolean isUserSetBrightness;
     field public final float lastBrightness;
     field public final long[] luxTimestamps;
     field public final float[] luxValues;
     field public final boolean nightMode;
     field public final java.lang.String packageName;
+    field public final float powerBrightnessFactor;
     field public final long timeStamp;
   }
 
@@ -2679,10 +2704,10 @@
 package android.media.tv {
 
   public final class TvContentRatingSystemInfo implements android.os.Parcelable {
-    method public static final android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
+    method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
     method public int describeContents();
-    method public final android.net.Uri getXmlUri();
-    method public final boolean isSystemDefined();
+    method public android.net.Uri getXmlUri();
+    method public boolean isSystemDefined();
     method public void writeToParcel(android.os.Parcel, int);
   }
 
@@ -4464,6 +4489,24 @@
 
 }
 
+package android.service.textclassifier {
+
+  public abstract class TextClassifierService extends android.app.Service {
+    ctor public TextClassifierService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onClassifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
+    method public abstract void onGenerateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
+    method public abstract void onSuggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
+  }
+
+  public static abstract interface TextClassifierService.Callback<T> {
+    method public abstract void onFailure(java.lang.CharSequence);
+    method public abstract void onSuccess(T);
+  }
+
+}
+
 package android.service.trust {
 
   public class TrustAgentService extends android.app.Service {
@@ -4645,15 +4688,15 @@
   }
 
   public final deprecated class Phone {
-    method public final void addListener(android.telecom.Phone.Listener);
-    method public final boolean canAddCall();
-    method public final deprecated android.telecom.AudioState getAudioState();
-    method public final android.telecom.CallAudioState getCallAudioState();
-    method public final java.util.List<android.telecom.Call> getCalls();
-    method public final void removeListener(android.telecom.Phone.Listener);
+    method public void addListener(android.telecom.Phone.Listener);
+    method public boolean canAddCall();
+    method public deprecated android.telecom.AudioState getAudioState();
+    method public android.telecom.CallAudioState getCallAudioState();
+    method public java.util.List<android.telecom.Call> getCalls();
+    method public void removeListener(android.telecom.Phone.Listener);
     method public void requestBluetoothAudio(java.lang.String);
-    method public final void setAudioRoute(int);
-    method public final void setMuted(boolean);
+    method public void setAudioRoute(int);
+    method public void setMuted(boolean);
   }
 
   public static abstract class Phone.Listener {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b63703d..48f43e0 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,13 +1,5 @@
 package android.app {
 
-  public abstract deprecated class EphemeralResolverService extends android.app.InstantAppResolverService {
-    ctor public EphemeralResolverService();
-    method public android.os.Looper getLooper();
-    method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
-    method public android.content.pm.EphemeralResolveInfo onGetEphemeralIntentFilter(java.lang.String);
-    method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]);
-  }
-
   public class Notification implements android.os.Parcelable {
     method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
   }
@@ -31,10 +23,7 @@
 
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
     field public static final deprecated java.lang.String ACTION_DEVICE_INITIALIZATION_WIZARD = "android.intent.action.DEVICE_INITIALIZATION_WIZARD";
-    field public static final deprecated java.lang.String ACTION_EPHEMERAL_RESOLVER_SETTINGS = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
-    field public static final deprecated java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
     field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
-    field public static final deprecated java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
     field public static final deprecated java.lang.String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
     field public static final deprecated java.lang.String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator";
     field public static final deprecated java.lang.String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator";
@@ -62,45 +51,6 @@
 
 }
 
-package android.content.pm {
-
-  public final deprecated class EphemeralIntentFilter implements android.os.Parcelable {
-    ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
-    method public int describeContents();
-    method public java.util.List<android.content.IntentFilter> getFilters();
-    method public java.lang.String getSplitName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR;
-  }
-
-  public final deprecated class EphemeralResolveInfo implements android.os.Parcelable {
-    ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
-    ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
-    ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>, int);
-    ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
-    method public int describeContents();
-    method public byte[] getDigestBytes();
-    method public int getDigestPrefix();
-    method public deprecated java.util.List<android.content.IntentFilter> getFilters();
-    method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
-    method public java.lang.String getPackageName();
-    method public int getVersionCode();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
-    field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
-  }
-
-  public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable {
-    ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String);
-    method public int describeContents();
-    method public byte[][] getDigestBytes();
-    method public int[] getDigestPrefix();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR;
-  }
-
-}
-
 package android.media.tv {
 
   public final class TvInputManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 4e8f904..92bf24d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -298,6 +298,15 @@
 
 package android.hardware.display {
 
+  public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+    method public int describeContents();
+    method public float[] getBucketBoundaries();
+    method public java.time.LocalDate getLocalDate();
+    method public float[] getStats();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+  }
+
   public final class BrightnessChangeEvent implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -305,11 +314,14 @@
     field public final float batteryLevel;
     field public final float brightness;
     field public final int colorTemperature;
+    field public final boolean isDefaultBrightnessConfig;
+    field public final boolean isUserSetBrightness;
     field public final float lastBrightness;
     field public final long[] luxTimestamps;
     field public final float[] luxValues;
     field public final boolean nightMode;
     field public final java.lang.String packageName;
+    field public final float powerBrightnessFactor;
     field public final long timeStamp;
   }
 
@@ -565,6 +577,14 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
+  public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
+    method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
+  }
+
+  public final class DateValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
+  }
+
   public final class FillResponse implements android.os.Parcelable {
     method public int getFlags();
   }
@@ -599,7 +619,8 @@
   }
 
   public abstract interface ValueFinder {
-    method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
+    method public default java.lang.String findByAutofillId(android.view.autofill.AutofillId);
+    method public abstract android.view.autofill.AutofillValue findRawValueByAutofillId(android.view.autofill.AutofillId);
   }
 
 }
@@ -986,8 +1007,8 @@
   }
 
   public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
-    method public final void setActionButton(int);
-    method public final void setButtonState(int);
+    method public void setActionButton(int);
+    method public void setButtonState(int);
   }
 
   public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index f75678b..6e0bd3a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -34,6 +35,7 @@
 
 import libcore.io.Streams;
 
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 
@@ -583,7 +585,7 @@
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
             try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
-                Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out);
+                FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
             }
         }
     }
@@ -596,7 +598,7 @@
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
             try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
-                Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor()));
+                FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
             }
         }
     }
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 44adaec..3f0e331 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -65,7 +65,7 @@
 bool
 PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; }
 
-PrivacySpec new_spec_from_args(int dest)
+PrivacySpec PrivacySpec::new_spec(int dest)
 {
     switch (dest) {
         case android::os::DEST_AUTOMATIC:
@@ -77,4 +77,7 @@
     }
 }
 
-PrivacySpec get_default_dropbox_spec() { return PrivacySpec(android::os::DEST_AUTOMATIC); }
\ No newline at end of file
+PrivacySpec PrivacySpec::get_default_dropbox_spec()
+{
+    return PrivacySpec(android::os::DEST_AUTOMATIC);
+}
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index 9e15ff4..4f3db67 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -65,8 +65,6 @@
     const uint8_t dest;
 
     PrivacySpec() : dest(DEST_DEFAULT_VALUE) {}
-    PrivacySpec(uint8_t dest) : dest(dest) {}
-
     bool operator<(const PrivacySpec& other) const;
 
     // check permission of a policy, if returns true, don't strip the data.
@@ -74,9 +72,12 @@
 
     // if returns true, no data need to be stripped.
     bool RequireAll() const;
-};
 
-PrivacySpec new_spec_from_args(int dest);
-PrivacySpec get_default_dropbox_spec();
+    // Constructs spec using static methods below.
+    static PrivacySpec new_spec(int dest);
+    static PrivacySpec get_default_dropbox_spec();
+private:
+    PrivacySpec(uint8_t dest) : dest(dest) {}
+};
 
 #endif // PRIVACY_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index bd559d6..b9f479b 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -64,7 +64,8 @@
 ReportRequestSet::ReportRequestSet()
     :mRequests(),
      mSections(),
-     mMainFd(-1)
+     mMainFd(-1),
+     mMainDest(-1)
 {
 }
 
@@ -86,6 +87,12 @@
     mMainFd = fd;
 }
 
+void
+ReportRequestSet::setMainDest(int dest)
+{
+    mMainDest = dest;
+}
+
 bool
 ReportRequestSet::containsSection(int id) {
     return mSections.containsSection(id);
@@ -125,12 +132,14 @@
     status_t err = NO_ERROR;
     bool needMainFd = false;
     int mainFd = -1;
+    int mainDest = -1;
     HeaderSection headers;
 
     // See if we need the main file
     for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
         if ((*it)->fd < 0 && mainFd < 0) {
             needMainFd = true;
+            mainDest = (*it)->args.dest();
             break;
         }
     }
@@ -154,6 +163,7 @@
 
         // Add to the set
         batch.setMainFd(mainFd);
+        batch.setMainDest(mainDest);
     }
 
     // Tell everyone that we're starting.
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 2615c62..f30ecf0 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -53,6 +53,7 @@
 
     void add(const sp<ReportRequest>& request);
     void setMainFd(int fd);
+    void setMainDest(int dest);
 
     typedef vector<sp<ReportRequest>>::iterator iterator;
 
@@ -61,10 +62,12 @@
 
     int mainFd() { return mMainFd; }
     bool containsSection(int id);
+    int mainDest() { return mMainDest; }
 private:
     vector<sp<ReportRequest>> mRequests;
     IncidentReportArgs mSections;
     int mMainFd;
+    int mMainDest;
 };
 
 // ================================================================================
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 0827785..faeab87 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -152,36 +152,40 @@
 
     // The streaming ones, group requests by spec in order to save unnecessary strip operations
     map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
-    for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
+    for (auto it = requests->begin(); it != requests->end(); it++) {
         sp<ReportRequest> request = *it;
         if (!request->ok() || !request->args.containsSection(id)) {
             continue;  // skip invalid request
         }
-        PrivacySpec spec = new_spec_from_args(request->args.dest());
+        PrivacySpec spec = PrivacySpec::new_spec(request->args.dest());
         requestsBySpec[spec].push_back(request);
     }
 
-    for (map<PrivacySpec, vector<sp<ReportRequest>>>::iterator mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
+    for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
         PrivacySpec spec = mit->first;
         err = privacyBuffer.strip(spec);
         if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted.
         if (privacyBuffer.size() == 0) continue;
 
-        for (vector<sp<ReportRequest>>::iterator it = mit->second.begin(); it != mit->second.end(); it++) {
+        for (auto it = mit->second.begin(); it != mit->second.end(); it++) {
             sp<ReportRequest> request = *it;
             err = write_section_header(request->fd, id, privacyBuffer.size());
             if (err != NO_ERROR) { request->err = err; continue; }
             err = privacyBuffer.flush(request->fd);
             if (err != NO_ERROR) { request->err = err; continue; }
             writeable++;
-            ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), request->fd, spec.dest);
+            ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id,
+                  privacyBuffer.size(), request->fd, spec.dest);
         }
         privacyBuffer.clear();
     }
 
     // The dropbox file
     if (requests->mainFd() >= 0) {
-        err = privacyBuffer.strip(get_default_dropbox_spec());
+        PrivacySpec spec = requests->mainDest() < 0 ?
+                PrivacySpec::get_default_dropbox_spec() :
+                PrivacySpec::new_spec(requests->mainDest());
+        err = privacyBuffer.strip(spec);
         if (err != NO_ERROR) return err; // the buffer data is corrupted.
         if (privacyBuffer.size() == 0) goto DONE;
 
@@ -190,7 +194,8 @@
         err = privacyBuffer.flush(requests->mainFd());
         if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
         writeable++;
-        ALOGD("Section %d flushed %zu bytes to dropbox %d", id, privacyBuffer.size(), requests->mainFd());
+        ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id,
+              privacyBuffer.size(), requests->mainFd(), spec.dest);
     }
 
 DONE:
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 32b9e42..c7bfe55 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -73,7 +73,7 @@
     }
 
     void assertStrip(uint8_t dest, string expected, Privacy* policy) {
-        PrivacySpec spec(dest);
+        PrivacySpec spec = PrivacySpec::new_spec(dest);
         EncodedBuffer::iterator bufData = buffer.data();
         PrivacyBuffer privacyBuf(policy, bufData);
         ASSERT_EQ(privacyBuf.strip(spec), NO_ERROR);
@@ -224,7 +224,8 @@
     Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
-    PrivacySpec spec1(DEST_EXPLICIT), spec2(DEST_LOCAL);
+    PrivacySpec spec1 = PrivacySpec::new_spec(DEST_EXPLICIT);
+    PrivacySpec spec2 = PrivacySpec::new_spec(DEST_LOCAL);
 
     ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR);
     assertBuffer(privacyBuf, STRING_FIELD_0);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index eabbb96..b0019ac 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,8 +38,11 @@
     src/external/StatsPuller.cpp \
     src/external/StatsCompanionServicePuller.cpp \
     src/external/SubsystemSleepStatePuller.cpp \
+    src/external/ResourceHealthManagerPuller.cpp \
     src/external/CpuTimePerUidPuller.cpp \
     src/external/CpuTimePerUidFreqPuller.cpp \
+    src/external/KernelUidCpuActiveTimeReader.cpp \
+    src/external/KernelUidCpuClusterTimeReader.cpp \
     src/external/StatsPullerManagerImpl.cpp \
     src/logd/LogEvent.cpp \
     src/logd/LogListener.cpp \
@@ -75,7 +78,8 @@
     $(LOCAL_PATH)/../../core/java
 
 statsd_common_static_libraries := \
-    libplatformprotos
+    libhealthhalutils \
+    libplatformprotos \
 
 statsd_common_shared_libraries := \
     libbase \
@@ -93,6 +97,7 @@
     libhidlbase \
     libhidltransport \
     libhwbinder \
+    android.hardware.health@2.0 \
     android.hardware.power@1.0 \
     android.hardware.power@1.1 \
     libmemunreachable
@@ -165,6 +170,7 @@
 
 LOCAL_SRC_FILES := \
     $(statsd_common_src) \
+    tests/dimension_test.cpp \
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
@@ -190,7 +196,8 @@
     tests/e2e/WakelockDuration_e2e_test.cpp \
     tests/e2e/MetricConditionLink_e2e_test.cpp \
     tests/e2e/Attribution_e2e_test.cpp \
-    tests/e2e/GaugeMetric_e2e_test.cpp
+    tests/e2e/GaugeMetric_e2e_test.cpp \
+    tests/e2e/DimensionInCondition_e2e_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 857a6dd..f0eaeff 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -67,12 +67,16 @@
     return hashDimensionsValue(0, value);
 }
 
+android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
+    android::hash_t hash = seed;
+    hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
+    return JenkinsHashWhiten(hash);
+}
+
 using std::string;
 
 string HashableDimensionKey::toString() const {
-    string flattened;
-    DimensionsValueToString(getDimensionsValue(), &flattened);
-    return flattened;
+    return DimensionsValueToString(getDimensionsValue());
 }
 
 bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
@@ -162,6 +166,22 @@
     return LessThan(getDimensionsValue(), that.getDimensionsValue());
 };
 
+string MetricDimensionKey::toString() const {
+    string flattened = mDimensionKeyInWhat.toString();
+    flattened += mDimensionKeyInCondition.toString();
+    return flattened;
+}
+
+bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
+    return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
+        mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+};
+
+bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
+    return toString().compare(that.toString()) < 0;
+};
+
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85c317f..a31d7a6 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -41,6 +41,10 @@
         return mDimensionsValue;
     }
 
+    inline DimensionsValue* getMutableDimensionsValue() {
+        return &mDimensionsValue;
+    }
+
     bool operator==(const HashableDimensionKey& that) const;
 
     bool operator<(const HashableDimensionKey& that) const;
@@ -53,8 +57,52 @@
     DimensionsValue mDimensionsValue;
 };
 
+class MetricDimensionKey {
+ public:
+    explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
+                                const HashableDimensionKey& dimensionKeyInCondition)
+        : mDimensionKeyInWhat(dimensionKeyInWhat),
+          mDimensionKeyInCondition(dimensionKeyInCondition) {};
+
+    MetricDimensionKey(){};
+
+    MetricDimensionKey(const MetricDimensionKey& that)
+        : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
+          mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+
+    MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
+
+    std::string toString() const;
+
+    inline const HashableDimensionKey& getDimensionKeyInWhat() const {
+        return mDimensionKeyInWhat;
+    }
+
+    inline const HashableDimensionKey& getDimensionKeyInCondition() const {
+        return mDimensionKeyInCondition;
+    }
+
+    bool hasDimensionKeyInCondition() const {
+        return mDimensionKeyInCondition.getDimensionsValue().has_field();
+    }
+
+    bool operator==(const MetricDimensionKey& that) const;
+
+    bool operator<(const MetricDimensionKey& that) const;
+
+    inline const char* c_str() const {
+        return toString().c_str();
+    }
+  private:
+      HashableDimensionKey mDimensionKeyInWhat;
+      HashableDimensionKey mDimensionKeyInCondition;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+
 android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
 android::hash_t hashDimensionsValue(const DimensionsValue& value);
+android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
 
 }  // namespace statsd
 }  // namespace os
@@ -63,6 +111,7 @@
 namespace std {
 
 using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::MetricDimensionKey;
 
 template <>
 struct hash<HashableDimensionKey> {
@@ -71,4 +120,14 @@
     }
 };
 
-}  // namespace std
+template <>
+struct hash<MetricDimensionKey> {
+    std::size_t operator()(const MetricDimensionKey& key) const {
+        android::hash_t hash = hashDimensionsValue(
+            key.getDimensionKeyInWhat().getDimensionsValue());
+        hash = android::JenkinsHashMix(hash,
+                    hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+        return android::JenkinsHashWhiten(hash);
+    }
+};
+}  // namespace std
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c19ff63..7642aafa 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -104,7 +104,10 @@
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
-
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f545bb0..4e41454 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -551,6 +551,12 @@
     return NO_ERROR;
 }
 
+status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+    mStatsPullerManager.ClearPullerCache();
+    fprintf(out, "Puller cached data removed!\n");
+    return NO_ERROR;
+}
+
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                       const vector<String16>& app) {
     VLOG("StatsService::informAllUidData was called");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index be20893..fd3ed1d 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -197,6 +197,11 @@
      */
     status_t cmd_dump_memory_info(FILE* out);
 
+  /*
+     * Clear all puller cached data
+     */
+  status_t cmd_clear_puller_cache(FILE* out);
+
     /**
      * Update a configuration.
      */
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ded6c4c..c84a5b4 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -96,7 +96,7 @@
     }
 }
 
-void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
                                    const int64_t& bucketNum) {
     flushPastBuckets(bucketNum);
 
@@ -147,7 +147,7 @@
     }
 }
 
-int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
                                            const int64_t& bucketNum) const {
     const auto& bucket = mPastBuckets[index(bucketNum)];
     if (bucket == nullptr) {
@@ -157,7 +157,7 @@
     return itr == bucket->end() ? 0 : itr->second;
 }
 
-int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
     const auto& itr = mSumOverPastBuckets.find(key);
     if (itr != mSumOverPastBuckets.end()) {
         return itr->second;
@@ -165,7 +165,7 @@
     return 0;
 }
 
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key,
                                    const int64_t& currentBucketValue) {
     if (currentBucketNum > mMostRecentBucketNum + 1) {
         // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
@@ -175,7 +175,7 @@
             && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) {
     // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
         VLOG("Skipping anomaly declaration since within refractory period");
@@ -199,14 +199,14 @@
 
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
-    // TODO: This should also take in the const HashableDimensionKey& key?
+    // TODO: This should also take in the const MetricDimensionKey& key?
     android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
                                mConfigKey.GetId(), mAlert.id());
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
                                              const int64_t& currBucketNum,
-                                             const HashableDimensionKey& key,
+                                             const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
         declareAnomaly(timestampNs, key);
@@ -214,7 +214,7 @@
 }
 
 bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
-                                          const HashableDimensionKey& key) {
+                                          const MetricDimensionKey& key) {
     const auto& it = mRefractoryPeriodEndsSec.find(key);
     if (it != mRefractoryPeriodEndsSec.end()) {
         if ((timestampNs / NS_PER_SEC) <= it->second) {
@@ -226,7 +226,7 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
     VLOG("informSubscribers called.");
     if (mSubscriptions.empty()) {
         ALOGE("Attempt to call with no subscribers.");
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 472c02c..f01a97f 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -48,19 +48,19 @@
     // Adds a bucket.
     // Bucket index starts from 0.
     void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
-    void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+    void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
                        const int64_t& bucketNum);
 
     // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
-    bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+    bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
+    void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key);
 
     // Detects the alert and informs the incidentd when applicable.
     void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
-                                 const HashableDimensionKey& key,
+                                 const MetricDimensionKey& key,
                                  const int64_t& currentBucketValue);
 
     // Init the AnomalyMonitor which is shared across anomaly trackers.
@@ -69,10 +69,10 @@
     }
 
     // Helper function to return the sum value of past buckets at given dimension.
-    int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+    int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
 
     // Helper function to return the value for a past bucket.
-    int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+    int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
 
     // Returns the anomaly threshold.
     inline int64_t getAnomalyThreshold() const {
@@ -81,7 +81,7 @@
 
     // Returns the refractory period timestamp (in seconds) for the given key.
     // If there is no stored refractory period ending timestamp, returns 0.
-    uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+    uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
         const auto& it = mRefractoryPeriodEndsSec.find(key);
         return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
     }
@@ -124,7 +124,7 @@
     // declared for that dimension) ends, in seconds. Only anomalies that occur after this period
     // ends will be declared.
     // Entries may be, but are not guaranteed to be, removed after the period is finished.
-    unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+    unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
 
     void flushPastBuckets(const int64_t& currBucketNum);
 
@@ -135,7 +135,7 @@
     // and remove any items with value 0.
     void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
 
-    bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
+    bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
 
     // Calculates the corresponding bucket index within the circular array.
     size_t index(int64_t bucketNum) const;
@@ -144,7 +144,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers that an anomaly has occurred.
-    void informSubscribers(const HashableDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 7576a38..bbee9fa 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -37,8 +37,8 @@
     if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!");
 }
 
-void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
-                                                  const uint64_t& timestampNs) {
+void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
+                                                          const uint64_t& timestampNs) {
     auto itr = mAlarms.find(dimensionKey);
     if (itr == mAlarms.end()) {
         return;
@@ -51,7 +51,7 @@
     }
 }
 
-void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
                                 const uint64_t& timestampNs) {
 
     uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
@@ -66,7 +66,7 @@
     }
 }
 
-void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
     auto itr = mAlarms.find(dimensionKey);
     if (itr != mAlarms.end()) {
         mAlarms.erase(dimensionKey);
@@ -77,7 +77,7 @@
 }
 
 void DurationAnomalyTracker::stopAllAlarms() {
-    std::set<HashableDimensionKey> keys;
+    std::set<MetricDimensionKey> keys;
     for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
         keys.insert(itr->first);
     }
@@ -95,7 +95,7 @@
     // seldomly called. The alternative would be having AnomalyAlarms store information about the
     // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is
     // rarely ever called.
-    unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+    unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
     for (const auto& kv : mAlarms) {
         if (firedAlarms.count(kv.second) > 0) {
             matchedAlarms.insert({kv.first, kv.second});
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 33e55ab..052fdf57 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -32,10 +32,10 @@
     virtual ~DurationAnomalyTracker();
 
     // Starts the alarm at the given timestamp.
-    void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+    void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime);
 
     // Stops the alarm.
-    void stopAlarm(const HashableDimensionKey& dimensionKey);
+    void stopAlarm(const MetricDimensionKey& dimensionKey);
 
     // Stop all the alarms owned by this tracker.
     void stopAllAlarms();
@@ -46,7 +46,7 @@
     }
 
     // Declares the anomaly when the alarm expired given the current timestamp.
-    void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+    void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
                                       const uint64_t& timestampNs);
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
@@ -59,7 +59,7 @@
 protected:
     // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
     // are still active.
-    std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+    std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
 
     // Anomaly alarm monitor.
     sp<AnomalyMonitor> mAnomalyMonitor;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b32af02..b156d8d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -93,11 +93,13 @@
         WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
         LmkStateChanged lmk_state_changed = 54;
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
+        ShutdownSequenceReported shutdown_sequence_reported = 56;
+        BootSequenceReported boot_sequence_reported = 57;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10019
+    // Next: 10021
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -118,6 +120,8 @@
         CpuActiveTime cpu_active_time = 10016;
         CpuClusterTime cpu_cluster_time = 10017;
         DiskSpace disk_space = 10018;
+        RemainingBatteryCapacity remaining_battery_capacity = 10019;
+        FullBatteryCapacity full_battery_capacity = 10020;
     }
 }
 
@@ -669,6 +673,56 @@
 }
 
 /**
+ * Logs shutdown reason and duration on next boot.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/server/BootReceiver.java
+ */
+message ShutdownSequenceReported {
+    // True if shutdown is for a reboot. Default: false if we do not know.
+    optional bool reboot = 1;
+
+    // Reason for shutdown. Eg: userrequested. Default: "<EMPTY>".
+    optional string reason = 2;
+
+    // Beginning of shutdown time in ms using wall clock time since unix epoch.
+    // Default: 0 if no start time received.
+    optional int64 start_time_ms = 3;
+
+    // Duration of shutdown in ms. Default: 0 if no duration received.
+    optional int64 duration_ms = 4;
+}
+
+
+/**
+ * Logs boot reason and duration.
+ *
+ * Logged from:
+ *   system/core/bootstat/bootstat.cpp
+ */
+message BootSequenceReported {
+    // Reason for bootloader boot. Eg. reboot. See bootstat.cpp for larger list
+    // Default: "<EMPTY>" if not available.
+    optional string bootloader_reason = 1;
+
+    // Reason for system boot. Eg. bootloader, reboot,userrequested
+    // Default: "<EMPTY>" if not available.
+    optional string system_reason = 2;
+
+    // End of boot time in ms from unix epoch using system wall clock.
+    optional int64 end_time_ms = 3;
+
+    // Total boot duration in ms.
+    optional int64 total_duration_ms = 4;
+
+    // Bootloader duration in ms.
+    optional int64 bootloader_duration_ms = 5;
+
+    // Time since last boot in ms. Default: 0 if not available.
+    optional int64 time_since_last_boot = 6;
+}
+
+/**
  * Logs phone signal strength changes.
  *
  * Logged from:
@@ -705,8 +759,8 @@
     // The tag used with the is_default for resetting sets of settings. This is generally null.
     optional string tag = 5;
 
-    // 1 indicates that this setting with tag should be resettable.
-    optional int32 is_default = 6;
+    // True if this setting with tag should be resettable.
+    optional bool is_default = 6;
 
     // The user ID associated. Defined in android/os/UserHandle.java
     optional int32 user = 7;
@@ -1053,9 +1107,12 @@
 
     optional int32 isolated_uid = 2;
 
-    // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
-    // be removed before if it's used for another parent uid.
-    optional int32 is_create = 3;
+    // We expect an isolated uid to be removed before if it's used for another parent uid.
+    enum Event {
+        REMOVED = 0;
+        CREATED = 1;
+    }
+    optional Event event = 3;
 }
 
 /**
@@ -1358,3 +1415,19 @@
     // available bytes in download cache or temp directories
     optional uint64 temp_available_bytes = 3;
 }
+
+/**
+ * Pulls battery coulomb counter, which is the remaining battery charge in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message RemainingBatteryCapacity {
+    optional int32 charge_uAh = 1;
+}
+
+/**
+ * Pulls battery capacity, which is the battery capacity when full in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message FullBatteryCapacity {
+    optional int32 capacity_uAh = 1;
+}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index ea6586c..4c20ccb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -78,6 +78,7 @@
             return false;
         }
 
+
         bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
                                                      conditionIdIndexMap, stack);
 
@@ -88,8 +89,10 @@
             ALOGW("Child initialization success %lld ", (long long)child);
         }
 
+        if (allConditionTrackers[childIndex]->isSliced()) {
+            setSliced(true);
+        }
         mChildren.push_back(childIndex);
-
         mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
                              childTracker->getLogTrackerIndex().end());
     }
@@ -105,11 +108,15 @@
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
-        vector<ConditionState>& conditionCache) const {
+        const FieldMatcher& dimensionFields,
+        vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    // So far, this is fine as there is at most one child having sliced output.
     for (const int childIndex : mChildren) {
         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
-                                                      conditionCache);
+                                                      dimensionFields, conditionCache,
+                                                      dimensionsKeySet);
         }
     }
     conditionCache[mIndex] =
@@ -127,6 +134,7 @@
     }
 
     for (const int childIndex : mChildren) {
+        // So far, this is fine as there is at most one child having sliced output.
         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
             const sp<ConditionTracker>& child = mAllConditions[childIndex];
             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
@@ -159,6 +167,24 @@
     }
 }
 
+ConditionState CombinationConditionTracker::getMetConditionDimension(
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
+    // So far, this is fine as there is at most one child having sliced output.
+    for (const int childIndex : mChildren) {
+        conditionCache[childIndex] = conditionCache[childIndex] |
+            allConditions[childIndex]->getMetConditionDimension(
+                allConditions, dimensionFields, dimensionsKeySet);
+    }
+    evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+    if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
+        dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
+    }
+    return conditionCache[mIndex];
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dfd3837..0b7f949 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,12 +41,20 @@
                            std::vector<ConditionState>& conditionCache,
                            std::vector<bool>& changedCache) override;
 
-    void isConditionMet(const ConditionKey& conditionParameters,
-                        const std::vector<sp<ConditionTracker>>& allConditions,
-                        std::vector<ConditionState>& conditionCache) const override;
+    void isConditionMet(
+        const ConditionKey& conditionParameters,
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 
+    ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 private:
     LogicalOperation mLogicalOperation;
+
     // Store index of the children Predicates.
     // 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
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 773860f..81abbdb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -24,6 +24,7 @@
 #include <log/logprint.h>
 #include <utils/RefBase.h>
 
+#include <unordered_set>
 #include <unordered_map>
 
 namespace android {
@@ -84,10 +85,19 @@
     // [allConditions]: all condition trackers. This is needed because the condition evaluation is
     //                  done recursively
     // [conditionCache]: the cache holding the condition evaluation values.
+    // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
+    //                    condition, it assumes that only one child predicate is sliced.
     virtual void isConditionMet(
             const ConditionKey& conditionParameters,
             const std::vector<sp<ConditionTracker>>& allConditions,
-            std::vector<ConditionState>& conditionCache) const = 0;
+            const FieldMatcher& dimensionFields,
+            std::vector<ConditionState>& conditionCache,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+
+    virtual ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
 
     // return the list of LogMatchingTracker index that this ConditionTracker uses.
     virtual const std::set<int>& getLogTrackerIndex() const {
@@ -98,6 +108,10 @@
         mSliced = mSliced | sliced;
     }
 
+    bool isSliced() const {
+        return mSliced;
+    }
+
 protected:
     const int64_t mConditionId;
 
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index d99c2cc..0427700 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 #include "ConditionWizard.h"
+#include <unordered_set>
 
 namespace android {
 namespace os {
@@ -23,14 +24,26 @@
 using std::string;
 using std::vector;
 
-ConditionState ConditionWizard::query(const int index,
-                                      const ConditionKey& parameters) {
+ConditionState ConditionWizard::query(
+    const int index, const ConditionKey& parameters,
+    const FieldMatcher& dimensionFields,
+    std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
+
     vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
 
-    mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
+    mAllConditions[index]->isConditionMet(
+        parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
     return cache[index];
 }
 
+ConditionState ConditionWizard::getMetConditionDimension(
+    const int index, const FieldMatcher& dimensionFields,
+    std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
+
+    return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
+                                 *dimensionsKeySet);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4ff5c07..b38b59f 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -41,7 +41,14 @@
     // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
     virtual ConditionState query(
             const int conditionIndex,
-            const ConditionKey& conditionParameters);
+            const ConditionKey& conditionParameters,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+
+    virtual ConditionState getMetConditionDimension(
+            const int index,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
 
 private:
     std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 5cfc349..25265d5 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -104,6 +104,9 @@
                                   vector<bool>& stack) {
     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
     // if the initialization was successful.
+    if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
+        setSliced(true);
+    }
     return mInitialized;
 }
 
@@ -234,11 +237,12 @@
          conditionChangedCache[mIndex] == true);
 }
 
-void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
-                                               const vector<MatchingState>& eventMatcherValues,
-                                               const vector<sp<ConditionTracker>>& mAllConditions,
-                                               vector<ConditionState>& conditionCache,
-                                               vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(
+        const LogEvent& event,
+        const vector<MatchingState>& eventMatcherValues,
+        const vector<sp<ConditionTracker>>& mAllConditions,
+        vector<ConditionState>& conditionCache,
+        vector<bool>& conditionChangedCache) {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %lld %d",
@@ -271,7 +275,7 @@
         if (mSliced) {
             // if the condition result is sliced. metrics won't directly get value from the
             // cache, so just set any value other than kNotEvaluated.
-            conditionCache[mIndex] = ConditionState::kUnknown;
+            conditionCache[mIndex] = mInitialValue;
         } else {
             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
             if (itr == mSlicedConditionState.end()) {
@@ -310,10 +314,8 @@
             vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
                                                              ConditionState::kNotEvaluated);
             vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
-
             handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
                 dimensionalConditionCache, dimensionalConditionChangedCache);
-
             OrConditionState(dimensionalConditionCache, &conditionCache);
             OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
         }
@@ -323,42 +325,73 @@
 void SimpleConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
-        vector<ConditionState>& conditionCache) const {
-    const auto pair = conditionParameters.find(mConditionId);
-
-    if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
-        ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
-              (long long)mConditionId);
-        conditionCache[mIndex] = mInitialValue;
+        const FieldMatcher& dimensionFields,
+        vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+        // it has been evaluated.
+        VLOG("Yes, already evaluated, %lld %d",
+            (long long)mConditionId, conditionCache[mIndex]);
         return;
     }
-    std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+    const auto pair = conditionParameters.find(mConditionId);
+
+    if (pair == conditionParameters.end()) {
+        ConditionState conditionState = ConditionState::kNotEvaluated;
+        if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
+            dimensionFields.field() == mOutputDimensions.field()) {
+            conditionState = conditionState | getMetConditionDimension(
+                allConditions, dimensionFields, dimensionsKeySet);
+        } else {
+            conditionState = conditionState | mInitialValue;
+            if (!mSliced) {
+                const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+                if (itr != mSlicedConditionState.end()) {
+                    ConditionState sliceState =
+                        itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+                    conditionState = conditionState | sliceState;
+                }
+            }
+        }
+        conditionCache[mIndex] = conditionState;
+        return;
+    }
+    std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
     const std::vector<HashableDimensionKey> &keys =
             (pair == conditionParameters.end()) ? defaultKeys : pair->second;
 
     ConditionState conditionState = ConditionState::kNotEvaluated;
-    for (const auto& key : keys) {
+    for (size_t i = 0; i < keys.size(); ++i) {
+        const HashableDimensionKey& key = keys[i];
         auto startedCountIt = mSlicedConditionState.find(key);
         if (startedCountIt != mSlicedConditionState.end()) {
-            conditionState = conditionState |
-                    (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+            ConditionState sliceState =
+                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+            conditionState = conditionState | sliceState;
+            if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+                HashableDimensionKey dimensionKey;
+                if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
+                                    dimensionKey.getMutableDimensionsValue())) {
+                    dimensionsKeySet.insert(dimensionKey);
+                }
+            }
         } else {
             // For unseen key, check whether the require dimensions are subset of sliced condition
             // output.
-            bool seenDimension = false;
+            conditionState = conditionState | mInitialValue;
             for (const auto& slice : mSlicedConditionState) {
-                if (IsSubDimension(slice.first.getDimensionsValue(),
-                                   key.getDimensionsValue())) {
-                    seenDimension = true;
-                    conditionState = conditionState |
-                        (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+                ConditionState sliceState =
+                    slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+                if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+                    conditionState = conditionState | sliceState;
+                    if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+                        HashableDimensionKey dimensionKey;
+                        if (getSubDimension(slice.first.getDimensionsValue(),
+                                            dimensionFields, dimensionKey.getMutableDimensionsValue())) {
+                            dimensionsKeySet.insert(dimensionKey);
+                        }
+                    }
                 }
-                if (conditionState == ConditionState::kTrue) {
-                    break;
-                }
-            }
-            if (!seenDimension) {
-                conditionState = conditionState | mInitialValue;
             }
         }
     }
@@ -366,6 +399,38 @@
     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
 }
 
+ConditionState SimpleConditionTracker::getMetConditionDimension(
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    ConditionState conditionState = mInitialValue;
+    if (!dimensionFields.has_field() ||
+        !mOutputDimensions.has_field() ||
+        dimensionFields.field() != mOutputDimensions.field()) {
+        const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+        if (itr != mSlicedConditionState.end()) {
+            ConditionState sliceState =
+                itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+            conditionState = conditionState | sliceState;
+        }
+        return conditionState;
+    }
+
+    for (const auto& slice : mSlicedConditionState) {
+        ConditionState sliceState =
+            slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        DimensionsValue dimensionsValue;
+        conditionState = conditionState | sliceState;
+        HashableDimensionKey dimensionKey;
+        if (sliceState == ConditionState::kTrue &&
+            getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
+                            dimensionKey.getMutableDimensionsValue())) {
+            dimensionsKeySet.insert(dimensionKey);
+        }
+    }
+    return conditionState;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 815b445..ce9a02d 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,7 +48,14 @@
 
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        std::vector<ConditionState>& conditionCache) const override;
+                        const FieldMatcher& dimensionFields,
+                        std::vector<ConditionState>& conditionCache,
+                        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+
+    ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 
 private:
     const ConfigKey mConfigKey;
@@ -73,7 +80,8 @@
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
-    void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+    void handleConditionEvent(const HashableDimensionKey& outputKey,
+                              bool matchStart,
                               std::vector<ConditionState>& conditionCache,
                               std::vector<bool>& changedCache);
 
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 3b2d480..0ab33cf 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -118,6 +118,9 @@
 
 void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
                                std::vector<Field> *allFields) {
+    if (matcher.has_position()) {
+        leafField->set_position_index(0);
+    }
     if (matcher.child_size() == 0) {
         allFields->push_back(*rootField);
         return;
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index 04445ca..8a2e871 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -253,6 +253,9 @@
 }
 
 void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+    if (!value.has_field()) {
+        return;
+    }
     *flattened += std::to_string(value.field());
     *flattened += ":";
     switch (value.value_case()) {
@@ -352,6 +355,46 @@
     }
 }
 
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+                     DimensionsValue* subDimension) {
+    if (!matcher.has_field()) {
+        return false;
+    }
+    if (matcher.field() != dimension.field()) {
+        return false;
+    }
+    if (matcher.child_size() <= 0) {
+        if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
+            dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
+            return false;
+        }
+        *subDimension = dimension;
+        return true;
+    } else {
+        if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
+            return false;
+        }
+        bool found_value = true;
+        auto value_tuple = dimension.value_tuple();
+        subDimension->set_field(dimension.field());
+        for (int i = 0; found_value && i < matcher.child_size(); ++i) {
+            int j = 0;
+            for (; j < value_tuple.dimensions_value_size(); ++j) {
+                if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
+                    break;
+                }
+            }
+            if (j < value_tuple.dimensions_value_size()) {
+                found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
+                    subDimension->mutable_value_tuple()->add_dimensions_value());
+            } else {
+                found_value = false;
+            }
+        }
+        return found_value;
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index e900c5e..138c6e9 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -63,6 +63,9 @@
 
 // Helper function to get long value from the DimensionsValue proto.
 long getLongFromDimenValue(const DimensionsValue& dimensionValue);
+
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+                    DimensionsValue* subDimension);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
new file mode 100644
index 0000000..72fb5ff
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include "external/ResourceHealthManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceHealthManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
+
+bool getHealthHal() {
+    if (gHealthHal == nullptr) {
+        gHealthHal = get_health_service();
+
+    }
+    return gHealthHal != nullptr;
+}
+
+ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
+}
+
+// TODO: add other health atoms (eg. Temperature).
+bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    if (!getHealthHal()) {
+        ALOGE("Health Hal not loaded");
+        return false;
+    }
+
+    uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
+    data->clear();
+    bool result_success = true;
+
+    Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
+        if (r != Result::SUCCESS) {
+            result_success = false;
+            return;
+        }
+        if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
+            auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp);
+            ptr->write(v.legacy.batteryChargeCounter);
+            ptr->init();
+            data->push_back(ptr);
+        } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
+            auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp);
+            ptr->write(v.legacy.batteryFullCharge);
+            ptr->init();
+            data->push_back(ptr);
+        } else {
+            ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
+        }
+    });
+    if (!result_success || !ret.isOk()) {
+        ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
+                ret.description().c_str());
+        if (!ret.isOk() && ret.isDeadObject()) {
+            gHealthHal = nullptr;
+        }
+        return false;
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
new file mode 100644
index 0000000..9b238eaf5
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads Ihealth.hal
+ */
+class ResourceHealthManagerPuller : public StatsPuller {
+public:
+    ResourceHealthManagerPuller(int tagId);
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index cadc535..da14434 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,6 +59,11 @@
     return ret;
 }
 
+void StatsPuller::ClearCache() {
+    lock_guard<std::mutex> lock(mLock);
+    mCachedData.clear();
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 47cc9f0..bc7c45f 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -38,6 +38,8 @@
 
     bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
 
+    void ClearCache();
+
 protected:
     // The atom tag id this puller pulls
     const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 00a1475..4826d96 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -50,10 +50,14 @@
         return mPullerManager.Pull(tagId, data);
     }
 
-    virtual void SetTimeBaseSec(const long timeBaseSec) {
+    void SetTimeBaseSec(const long timeBaseSec) {
         mPullerManager.SetTimeBaseSec(timeBaseSec);
     }
 
+    void ClearPullerCache() {
+        mPullerManager.ClearPullerCache();
+    }
+
  private:
     StatsPullerManagerImpl
         & mPullerManager = StatsPullerManagerImpl::GetInstance();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 148c9ae..71b0abe 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,10 +23,11 @@
 #include <climits>
 #include "CpuTimePerUidFreqPuller.h"
 #include "CpuTimePerUidPuller.h"
-#include "SubsystemSleepStatePuller.h"
+#include "ResourceHealthManagerPuller.h"
 #include "StatsCompanionServicePuller.h"
 #include "StatsPullerManagerImpl.h"
 #include "StatsService.h"
+#include "SubsystemSleepStatePuller.h"
 #include "logd/LogEvent.h"
 #include "statslog.h"
 
@@ -83,7 +84,10 @@
              make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
     mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
                      make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-
+    mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
+                     make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
+    mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
+                     make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
 
@@ -195,6 +199,12 @@
     }
 }
 
+void StatsPullerManagerImpl::ClearPullerCache() {
+    for (auto puller : mPullers) {
+        puller.second->ClearCache();
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 7c59f66..fba3ade 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -49,6 +49,8 @@
 
     void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
 
+    void ClearPullerCache();
+
 private:
     StatsPullerManagerImpl();
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0455f6a..ae4df3e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,6 +21,7 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
+#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -71,13 +72,15 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -99,7 +102,10 @@
     auto count_metrics = report->mutable_count_metrics();
     for (const auto& counter : mPastBuckets) {
         CountMetricData* metricData = count_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            counter.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            counter.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : counter.second) {
             CountBucketInfo* bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -123,17 +129,26 @@
     VLOG("metric %lld dump report now...",(long long)mMetricId);
 
     for (const auto& counter : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = counter.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = counter.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
 
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
-        long long dimensionToken = protoOutput->start(
+        long long dimensionInWhatToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
-        protoOutput->end(dimensionToken);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionInWhatToken);
+
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
 
         // Then fill bucket_info (CountBucketInfo).
         for (const auto& bucket : counter.second) {
@@ -166,7 +181,7 @@
     mCondition = conditionMet;
 }
 
-bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
         return false;
     }
@@ -187,7 +202,7 @@
 }
 
 void CountMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 061b7a3..8659d47 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -50,7 +50,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -74,14 +74,14 @@
     void flushIfNeededLocked(const uint64_t& newEventTime);
 
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket.
     std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
 
     static const size_t kBucketSize = sizeof(CountBucket{});
 
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 000874c..efbdae1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -21,6 +21,7 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
+#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -81,13 +82,15 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -113,15 +116,17 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        const HashableDimensionKey& eventKey) const {
+        const MetricDimensionKey& eventKey) const {
     switch (mAggregationType) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
-                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+                    mDimensionsInCondition, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
-                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+                    mDimensionsInCondition, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
     }
 }
@@ -129,10 +134,34 @@
 void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
     flushIfNeededLocked(eventTime);
+
     // Now for each of the on-going event, check if the condition has changed for them.
-    for (auto& pair : mCurrentSlicedDuration) {
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
         pair.second->onSlicedConditionMayChange(eventTime);
     }
+
+
+    std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+    ConditionState conditionState = mWizard->getMetConditionDimension(
+        mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet);
+
+    bool condition = (conditionState == ConditionState::kTrue);
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+        conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+    }
+    std::unordered_set<MetricDimensionKey> newKeys;
+    for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
+        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+            auto newKey =
+                MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
+            if (newKeys.find(newKey) == newKeys.end()) {
+                mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
+                mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
+                mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+            }
+            newKeys.insert(newKey);
+        }
+    }
 }
 
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -142,7 +171,7 @@
     flushIfNeededLocked(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
-    for (auto& pair : mCurrentSlicedDuration) {
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
         pair.second->onConditionChanged(conditionMet, eventTime);
     }
 }
@@ -155,7 +184,10 @@
     auto duration_metrics = report->mutable_duration_metrics();
     for (const auto& pair : mPastBuckets) {
         DurationMetricData* metricData = duration_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            pair.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            pair.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : pair.second) {
             auto bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -179,8 +211,8 @@
     VLOG("metric %lld dump report now...", (long long)mMetricId);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = pair.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
 
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -188,9 +220,18 @@
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
 
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
+
         // Then fill bucket_info (DurationBucketInfo).
         for (const auto& bucket : pair.second) {
             long long bucketInfoToken = protoOutput->start(
@@ -219,10 +260,11 @@
         return;
     }
     VLOG("flushing...........");
-    for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
+    for (auto it = mCurrentSlicedDurationTrackerMap.begin();
+            it != mCurrentSlicedDurationTrackerMap.end();) {
         if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
             VLOG("erase bucket for key %s", it->first.c_str());
-            it = mCurrentSlicedDuration.erase(it);
+            it = mCurrentSlicedDurationTrackerMap.erase(it);
         } else {
             ++it;
         }
@@ -234,28 +276,28 @@
 }
 
 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
-    if (mCurrentSlicedDuration.size() == 0) {
+    if (mCurrentSlicedDurationTrackerMap.size() == 0) {
         return;
     }
 
     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
-            (unsigned long)mCurrentSlicedDuration.size());
+            (unsigned long)mCurrentSlicedDurationTrackerMap.size());
     if (verbose) {
-        for (const auto& slice : mCurrentSlicedDuration) {
+        for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
             fprintf(out, "\t%s\n", slice.first.c_str());
             slice.second->dumpStates(out, verbose);
         }
     }
 }
 
-bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     // the key is not new, we are good.
-    if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+    if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
         return false;
     }
     // 1. Report the tuple count if the tuple count > soft limit
-    if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
-        size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+    if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -268,27 +310,26 @@
 }
 
 void DurationMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKeys, bool condition,
         const LogEvent& event) {
     flushIfNeededLocked(event.GetTimestampNs());
 
     if (matcherIndex == mStopAllIndex) {
-        for (auto& pair : mCurrentSlicedDuration) {
+        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
             pair.second->noteStopAll(event.GetTimestampNs());
         }
         return;
     }
 
-
-    if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+    if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
+        mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
     }
 
-    auto it = mCurrentSlicedDuration.find(eventKey);
+    auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
 
     std::vector<DimensionsValue> values;
     getDimensionKeys(event, mInternalDimensions, &values);
@@ -302,10 +343,11 @@
     } else {
         for (const DimensionsValue& value : values) {
             if (matcherIndex == mStartIndex) {
-                it->second->noteStart(HashableDimensionKey(value), condition,
-                                      event.GetTimestampNs(), conditionKeys);
+                it->second->noteStart(
+                    HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
             } else if (matcherIndex == mStopIndex) {
-                it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+                it->second->noteStop(
+                   HashableDimensionKey(value), event.GetTimestampNs(), false);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index d8cab92..152e570 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKeys, bool condition,
             const LogEvent& event) override;
 
@@ -92,21 +92,21 @@
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
 
     // The current bucket.
-    std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
-            mCurrentSlicedDuration;
+    std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+            mCurrentSlicedDurationTrackerMap;
 
     // Helper function to create a duration tracker given the metric aggregation type.
     std::unique_ptr<DurationTracker> createDurationTracker(
-            const HashableDimensionKey& eventKey) const;
+        const MetricDimensionKey& eventKey) const;
 
     // This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 25c86d0..820d591 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -128,7 +128,7 @@
 }
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (!condition) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 9da0dd0..935f206 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -45,7 +45,7 @@
 
 private:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1072c5a..d6cb189 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -82,13 +82,15 @@
     mFieldFilter = metric.gauge_fields_filter();
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     // Kicks off the puller immediately.
     if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -136,18 +138,27 @@
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
+        const MetricDimensionKey& dimensionKey = pair.first;
 
-        VLOG("  dimension key %s", hashableKey.c_str());
+        VLOG("  dimension key %s", dimensionKey.c_str());
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
 
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
+
         // Then fill bucket_info (GaugeBucketInfo).
         for (const auto& bucket : pair.second) {
             long long bucketInfoToken = protoOutput->start(
@@ -248,7 +259,7 @@
     }
 }
 
-bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
         return false;
     }
@@ -268,7 +279,7 @@
 }
 
 void GaugeMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (condition == false) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6c01347..86d0ccd 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -44,7 +44,7 @@
     uint64_t mBucketNum;
 };
 
-typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
     DimToGaugeAtomsMap;
 
 // This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -64,7 +64,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -99,7 +99,7 @@
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current bucket.
     std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
@@ -119,7 +119,7 @@
     std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(GaugeBucket{});
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index e74924a..85e655b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -15,6 +15,8 @@
  */
 #include "MetricProducer.h"
 
+#include "dimension.h"
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -30,29 +32,51 @@
 
     bool condition;
     ConditionKey conditionKey;
+
+    std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mConditionLinks) {
             getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
         }
-        if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
-            condition = false;
-        } else {
-            condition = true;
-        }
+        auto conditionState =
+            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+                           &dimensionKeysInCondition);
+        condition = (conditionState == ConditionState::kTrue);
     } else {
         condition = mCondition;
     }
 
-    if (mDimensions.has_field() && mDimensions.child_size() > 0) {
-        vector<DimensionsValue> dimensionValues;
-        getDimensionKeys(event, mDimensions, &dimensionValues);
-        for (const DimensionsValue& dimensionValue : dimensionValues) {
+    vector<DimensionsValue> dimensionInWhatValues;
+    if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
+        getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+    }
+
+    if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
+        onMatchedLogEventInternalLocked(
+            matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
+    } else if (dimensionKeysInCondition.empty()) {
+        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
             onMatchedLogEventInternalLocked(
-                matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
+                matcherIndex,
+                MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY),
+                conditionKey, condition, event);
+        }
+    } else if (dimensionInWhatValues.empty()) {
+        for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+            onMatchedLogEventInternalLocked(
+                matcherIndex,
+                MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
+                conditionKey, condition, event);
         }
     } else {
-        onMatchedLogEventInternalLocked(
-            matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
+        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+            for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+                onMatchedLogEventInternalLocked(
+                    matcherIndex,
+                    MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
+                    conditionKey, condition, event);
+            }
+        }
     }
 }
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6f33073..3b1498f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -91,6 +91,7 @@
         std::lock_guard<std::mutex> lock(mMutex);
         return onDumpReportLocked(dumpTimeNs, protoOutput);
     }
+
     void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
         std::lock_guard<std::mutex> lock(mMutex);
         return onDumpReportLocked(dumpTimeNs, report);
@@ -156,7 +157,8 @@
 
     int mConditionTrackerIndex;
 
-    FieldMatcher mDimensions;  // The dimension defined in statsd_config
+    FieldMatcher mDimensionsInWhat;  // The dimensions_in_what defined in statsd_config
+    FieldMatcher mDimensionsInCondition;  // The dimensions_in_condition defined in statsd_config
 
     std::vector<MetricConditionLink> mConditionLinks;
 
@@ -178,7 +180,7 @@
      * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
      */
     virtual void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) = 0;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9cdbafc..d4b9102 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -143,6 +143,10 @@
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ae0c673..c9cc7bb 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -81,13 +81,15 @@
     }
 
     mBucketSizeNs = bucketSizeMills * 1000000;
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     if (!metric.has_condition() && mPullTagId != -1) {
         VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -124,7 +126,10 @@
     auto value_metrics = report->mutable_value_metrics();
     for (const auto& pair : mPastBuckets) {
         ValueMetricData* metricData = value_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            pair.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            pair.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : pair.second) {
             ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -146,16 +151,24 @@
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = pair.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
             FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
 
         // Then fill bucket_info (ValueBucketInfo).
         for (const auto& bucket : pair.second) {
@@ -239,7 +252,7 @@
     }
 }
 
-bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -260,7 +273,7 @@
 }
 
 void ValueMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9f750cf..121ec7d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -99,16 +99,16 @@
         long sum;
     } Interval;
 
-    std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+    std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
     std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index c2d2cea..45735a8 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,8 +60,9 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
-                    sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+    DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+                    sp<ConditionWizard> wizard, int conditionIndex,
+                    const FieldMatcher& dimensionInCondition, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
@@ -70,6 +71,7 @@
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
+          mDimensionInCondition(dimensionInCondition),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
           mDuration(0),
@@ -79,6 +81,8 @@
 
     virtual ~DurationTracker(){};
 
+    virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0;
+
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -92,7 +96,7 @@
     // events, so that the owner can safely remove the tracker.
     virtual bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
@@ -100,6 +104,10 @@
     // Dump internal states for debugging
     virtual void dumpStates(FILE* out, bool verbose) const = 0;
 
+    void setEventKey(const MetricDimensionKey& eventKey) {
+         mEventKey = eventKey;
+    }
+
 protected:
     // Starts the anomaly alarm.
     void startAnomalyAlarm(const uint64_t eventTime) {
@@ -150,7 +158,7 @@
 
     const int64_t mTrackerId;
 
-    HashableDimensionKey mEventKey;
+    MetricDimensionKey mEventKey;
 
     sp<ConditionWizard> mWizard;
 
@@ -158,6 +166,8 @@
 
     const int64_t mBucketSizeNs;
 
+    const FieldMatcher mDimensionInCondition;
+
     const bool mNested;
 
     uint64_t mCurrentBucketStartTimeNs;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 412a0c9..db7dea4 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -25,13 +25,23 @@
 namespace statsd {
 
 MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
-                                       const HashableDimensionKey& eventKey,
-                                       sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                                       const MetricDimensionKey& eventKey,
+                                       sp<ConditionWizard> wizard, int conditionIndex,
+                                       const FieldMatcher& dimensionInCondition, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        bool conditionSliced,
                                        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, conditionSliced, anomalyTrackers) {
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+                      currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) {
+}
+
+unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
+    auto clonedTracker = make_unique<MaxDurationTracker>(*this);
+    for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
+        it->second.lastStartTime = eventTime;
+        it->second.lastDuration = 0;
+    }
+    return clonedTracker;
 }
 
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -44,7 +54,7 @@
     if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mInfos.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
             newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -149,7 +159,7 @@
 }
 
 bool MaxDurationTracker::flushIfNeeded(
-        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+        uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return false;
     }
@@ -236,8 +246,14 @@
         if (pair.second.state == kStopped) {
             continue;
         }
-        bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
-                            ConditionState::kTrue;
+        std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+        ConditionState conditionState = mWizard->query(
+            mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
+            &conditionDimensionKeySet);
+        bool conditionMet = (conditionState == ConditionState::kTrue) &&
+            (!mDimensionInCondition.has_field() ||
+             conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+                conditionDimensionKeySet.end());
         VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
         noteConditionChanged(pair.first, conditionMet, timestamp);
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 661d131..4d32a06 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,10 +29,15 @@
 class MaxDurationTracker : public DurationTracker {
 public:
     MaxDurationTracker(const ConfigKey& key, const int64_t& id,
-                       const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                       int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                       uint64_t bucketSizeNs, bool conditionSliced,
+                       const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                       int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+                       uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                        const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+
+    MaxDurationTracker(const MaxDurationTracker& tracker) = default;
+
+    unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -41,7 +46,7 @@
 
     bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 75d7c08..0feae36 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,17 +25,25 @@
 using std::pair;
 
 OringDurationTracker::OringDurationTracker(
-        const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
-        sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+        const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+        sp<ConditionWizard> wizard, int conditionIndex,
+        const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs,
         uint64_t bucketSizeNs, bool conditionSliced,
         const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, conditionSliced, anomalyTrackers),
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+                      currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
 }
 
+unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
+    auto clonedTracker = make_unique<OringDurationTracker>(*this);
+    clonedTracker->mLastStartTime = eventTime;
+    clonedTracker->mDuration = 0;
+    return clonedTracker;
+}
+
 bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
@@ -45,7 +53,7 @@
     if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mConditionKeyMap.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
             newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -76,7 +84,6 @@
     if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
         mConditionKeyMap[key] = conditionKey;
     }
-
     VLOG("Oring: %s start, condition %d", key.c_str(), condition);
 }
 
@@ -128,7 +135,7 @@
 }
 
 bool OringDurationTracker::flushIfNeeded(
-        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+        uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
     if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return false;
     }
@@ -184,8 +191,14 @@
                 ++it;
                 continue;
             }
-            if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
-                ConditionState::kTrue) {
+            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+            ConditionState conditionState =
+                mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+                               mDimensionInCondition, &conditionDimensionKeySet);
+            if (conditionState != ConditionState::kTrue ||
+                (mDimensionInCondition.has_field() &&
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
+                    conditionDimensionKeySet.end())) {
                 startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
                 VLOG("Key %s started -> paused", key.c_str());
@@ -210,8 +223,14 @@
                 ++it;
                 continue;
             }
-            if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
-                ConditionState::kTrue) {
+            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+            ConditionState conditionState =
+                mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+                               mDimensionInCondition, &conditionDimensionKeySet);
+            if (conditionState == ConditionState::kTrue &&
+                (!mDimensionInCondition.has_field() ||
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
+                    != conditionDimensionKeySet.end())) {
                 pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
                 VLOG("Key %s paused -> started", key.c_str());
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43469ca..75b5a81 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,11 +28,15 @@
 class OringDurationTracker : public DurationTracker {
 public:
     OringDurationTracker(const ConfigKey& key, const int64_t& id,
-                         const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                         int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                         uint64_t bucketSizeNs, bool conditionSliced,
+                         const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                         int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+                         uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                          const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
 
+    OringDurationTracker(const OringDurationTracker& tracker) = default;
+
+    unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -44,7 +48,7 @@
 
     bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a41f30c..6c61400 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -54,6 +54,9 @@
 
 void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
                                        ProtoOutputStream* protoOutput) {
+    if (!dimensionsValue.has_field()) {
+        return;
+    }
     protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
     switch (dimensionsValue.value_case()) {
         case DimensionsValue::ValueCase::kValueStr:
@@ -103,6 +106,9 @@
 
 void writeFieldProtoToStream(
     const Field& field, util::ProtoOutputStream* protoOutput) {
+    if (!field.has_field()) {
+        return;
+    }
     protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
     if (field.has_position_index()) {
       protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 160b1f4..31f51a7 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -28,13 +28,14 @@
 namespace statsd {
 
 const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
+const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
 
 // Minimum bucket size in seconds
 const long kMinBucketSizeSec = 5 * 60;
 
 typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
 
-typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index f912e4b..3af684f 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -56,7 +56,7 @@
 
 void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
                                                   const Subscription& subscription,
-                                                  const HashableDimensionKey& dimKey) const {
+                                                  const MetricDimensionKey& dimKey) const {
     // Reminder about ids:
     //  subscription id - name of the Subscription (that ties the Alert to the broadcast)
     //  subscription rule_id - the name of the Alert (that triggers the broadcast)
@@ -92,7 +92,7 @@
 void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
                                              const ConfigKey& configKey,
                                              const Subscription& subscription,
-                                             const HashableDimensionKey& dimKey) const {
+                                             const MetricDimensionKey& dimKey) const {
     VLOG("SubscriberReporter::sendBroadcastLocked called.");
     if (mStatsCompanionService == nullptr) {
         ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
@@ -107,8 +107,8 @@
 }
 
 StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
-        const HashableDimensionKey& dimKey) {
-    return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+        const MetricDimensionKey& dimKey) {
+    return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
 }
 
 StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 5bb458a..13fc7fd 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -80,7 +80,7 @@
      */
     void alertBroadcastSubscriber(const ConfigKey& configKey,
                                   const Subscription& subscription,
-                                  const HashableDimensionKey& dimKey) const;
+                                  const MetricDimensionKey& dimKey) const;
 
 private:
     SubscriberReporter() {};
@@ -101,7 +101,7 @@
     void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
                              const ConfigKey& configKey,
                              const Subscription& subscription,
-                             const HashableDimensionKey& dimKey) const;
+                             const MetricDimensionKey& dimKey) const;
 
     /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
     static StatsDimensionsValue protoToStatsDimensionsValue(
@@ -109,7 +109,7 @@
 
     /** Converts a HashableDimensionKey to a StatsDimensionsValue. */
     static StatsDimensionsValue protoToStatsDimensionsValue(
-            const HashableDimensionKey& dimKey);
+            const MetricDimensionKey& dimKey);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 66bfa68..a415ea1 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -33,14 +33,14 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
-HashableDimensionKey getMockDimensionKey(int key, string value) {
+MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
     DimensionsValue dimensionsValue;
     dimensionsValue.set_field(key);
     dimensionsValue.set_value_str(value);
-    return HashableDimensionKey(dimensionsValue);
+    return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
 }
 
-void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
+void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
                       std::shared_ptr<DimToValMap> bucket) {
     for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
         (*bucket)[itr->first] += itr->second;
@@ -48,7 +48,7 @@
 }
 
 std::shared_ptr<DimToValMap> MockBucket(
-        const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
+        const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
     std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
     AddValueToBucket(key_value_pair_list, bucket);
     return bucket;
@@ -56,7 +56,7 @@
 
 // Returns the value, for the given key, in that bucket, or 0 if not present.
 int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
-                       const HashableDimensionKey& key) {
+                       const MetricDimensionKey& key) {
     const auto& itr = bucket->find(key);
     if (itr != bucket->end()) {
         return itr->second;
@@ -68,14 +68,14 @@
 bool detectAnomaliesPass(AnomalyTracker& tracker,
                          const int64_t& bucketNum,
                          const std::shared_ptr<DimToValMap>& currentBucket,
-                         const std::set<const HashableDimensionKey>& trueList,
-                         const std::set<const HashableDimensionKey>& falseList) {
-    for (HashableDimensionKey key : trueList) {
+                         const std::set<const MetricDimensionKey>& trueList,
+                         const std::set<const MetricDimensionKey>& falseList) {
+    for (MetricDimensionKey key : trueList) {
         if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
     }
-    for (HashableDimensionKey key : falseList) {
+    for (MetricDimensionKey key : falseList) {
         if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
@@ -100,7 +100,7 @@
 void checkRefractoryTimes(AnomalyTracker& tracker,
                           const int64_t& currTimestampNs,
                           const int32_t& refractoryPeriodSec,
-                          const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+                          const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
     for (const auto& kv : timestamps) {
         if (kv.second < 0) {
             // Make sure that, if there is a refractory period, it is already past.
@@ -124,9 +124,9 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
-    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
-    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
-    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+    MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+    MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+    MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
 
     int64_t eventTimestamp0 = 10 * NS_PER_SEC;
     int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
@@ -269,11 +269,11 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
-    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
-    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
-    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
-    HashableDimensionKey keyD = getMockDimensionKey(1, "d");
-    HashableDimensionKey keyE = getMockDimensionKey(1, "e");
+    MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+    MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+    MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
+    MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
+    MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
 
     std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
     std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 819f2be..d1b7b28 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -78,7 +78,7 @@
 std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
     const Position position,
     const std::vector<int> &uids, const string& conditionName) {
-    std::map<int64_t, std::vector<HashableDimensionKey>>  outputKeyMap;
+    std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
     std::vector<int> uid_indexes;
     switch(position) {
         case Position::FIRST:
@@ -265,6 +265,9 @@
 TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
     for (Position position :
             { Position::ANY, Position::FIRST, Position::LAST}) {
+        FieldMatcher dimensionInCondition;
+        std::unordered_set<HashableDimensionKey> dimensionKeys;
+
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
                 position);
@@ -307,7 +310,8 @@
         const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by this uid
@@ -361,7 +365,8 @@
 
         // query again
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
     }
@@ -369,6 +374,9 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+    FieldMatcher dimensionInCondition;
+    std::unordered_set<HashableDimensionKey> dimensionKeys;
+
     SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
             Position::ANY /* position */);
@@ -410,7 +418,8 @@
     ConditionKey queryKey;
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                    conditionCache, dimensionKeys);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by this uid
@@ -452,13 +461,17 @@
 
     // query again
     conditionCache[0] = ConditionState::kNotEvaluated;
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+    dimensionKeys.clear();
+    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                    conditionCache, dimensionKeys);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
 TEST(SimpleConditionTrackerTest, TestStopAll) {
     for (Position position :
             {Position::ANY, Position::FIRST, Position::LAST}) {
+        FieldMatcher dimensionInCondition;
+        std::unordered_set<HashableDimensionKey> dimensionKeys;
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
                 position);
@@ -502,7 +515,8 @@
         const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by uid2
@@ -528,8 +542,9 @@
         // TEST QUERY
         const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
 
@@ -550,15 +565,15 @@
         // TEST QUERY
         const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
         // TEST QUERY
         const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
     }
 
diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp
new file mode 100644
index 0000000..678abae
--- /dev/null
+++ b/cmds/statsd/tests/dimension_test.cpp
@@ -0,0 +1,149 @@
+// 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 "dimension.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+
+TEST(DimensionTest, subLeafNodes) {
+    DimensionsValue dimension;
+    int tagId = 100;
+    dimension.set_field(tagId);
+    auto child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(1);
+    child->set_value_int(2000);
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(3);
+    child->set_value_str("test");
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(4);
+    auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
+    grandChild->set_field(1);
+    grandChild->set_value_float(1.3f);
+    grandChild = child->mutable_value_tuple()->add_dimensions_value();
+    grandChild->set_field(3);
+    grandChild->set_value_str("tag");
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(6);
+    child->set_value_bool(false);
+
+    DimensionsValue sub_dimension;
+    FieldMatcher matcher;
+
+    // Tag id not matched.
+    matcher.set_field(tagId + 1);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field not exist.
+    matcher.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(5);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(6);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Not leaf field.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(4);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Grand-child leaf field not exist.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    auto childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(2);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Grand-child leaf field.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Multiple grand-child fields.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Multiple fields.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    matcher.add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Subset of the fields not exist.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    matcher.add_child()->set_field(2);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
new file mode 100644
index 0000000..b5d48ef
--- /dev/null
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -0,0 +1,725 @@
+// 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateCountMetricWithNoLinkConfig() {
+    StatsdConfig config;
+    auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = screenIsOffPredicate;
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by any attribution node and both by uid and tag.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+        CreateAttributionUidAndTagDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
+
+    auto metric = config.add_count_metric();
+    metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
+    metric->set_what(screenBrightnessChangeAtomMatcher.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+            android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    metric->set_bucket(ONE_MINUTE);
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
+    ConfigKey cfgKey;
+    auto config = CreateCountMetricWithNoLinkConfig();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    std::vector<AttributionNode> attributions1 =
+        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(222, "GMSCoreModule2")};
+
+    std::vector<AttributionNode> attributions2 =
+        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(555, "GMSCoreModule2")};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+
+    events.push_back(CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 200));
+    events.push_back(CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+
+    events.push_back(CreateAcquireWakelockEvent(
+        attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
+    events.push_back(CreateReleaseWakelockEvent(
+        attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 11));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 101));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 201));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + 203));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs - 99));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs - 2));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs + 2));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+    EXPECT_EQ(countMetrics.data_size(), 7);
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 3);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+    data = countMetrics.data(5);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(6);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+}
+
+namespace {
+
+StatsdConfig CreateCountMetricWithLinkConfig() {
+    StatsdConfig config;
+    auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+    *config.add_atom_matcher() = appCrashMatcher;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field*/);
+
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_count_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("AppCrashMetric"));
+    metric->set_what(appCrashMatcher.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+            android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+    // Links between crash atom and condition of app is in syncing.
+    auto links = metric->add_links();
+    links->set_condition(isSyncingPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
+    ConfigKey cfgKey;
+    auto config = CreateCountMetricWithLinkConfig();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    std::vector<AttributionNode> attributions1 =
+        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(222, "GMSCoreModule2")};
+
+    std::vector<AttributionNode> attributions2 =
+        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(555, "GMSCoreModule2")};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
+    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
+
+    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
+    events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
+
+    events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
+
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+
+    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+        bucketStartTimeNs + bucketSizeNs + 300));
+
+    events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+    events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+        bucketStartTimeNs + bucketSizeNs - 1));
+
+    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
+    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+        bucketStartTimeNs + bucketSizeNs + 600));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+    EXPECT_EQ(countMetrics.data_size(), 5);
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+    ValidateAttributionUidAndTagDimension(
+        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+    ValidateAttributionUidAndTagDimension(
+        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+    *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field */);
+
+    *config.add_predicate() = inBatterySaverModePredicate;
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_duration_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("BatterySaverModeDurationMetric"));
+    metric->set_what(inBatterySaverModePredicate.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
+    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+        ConfigKey cfgKey;
+        auto config = CreateDurationMetricConfigNoLink(aggregationType);
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        std::vector<AttributionNode> attributions1 =
+            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        std::vector<AttributionNode> attributions2 =
+            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(555, "GMSCoreModule2")};
+
+        std::vector<std::unique_ptr<LogEvent>> events;
+
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
+
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 300));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+            bucketStartTimeNs + bucketSizeNs - 1));
+
+        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 700));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+
+        ConfigMetricsReportList reports;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        StatsLogReport::DurationMetricDataWrapper metrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+        EXPECT_EQ(metrics.data_size(), 3);
+        auto data = metrics.data(0);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        EXPECT_FALSE(data.dimensions_in_condition().has_field());
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(1);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(2);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    }
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field */);
+
+    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+        CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    *config.add_predicate() = isInBackgroundPredicate;
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_duration_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("AppInBackgroundMetric"));
+    metric->set_what(isInBackgroundPredicate.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+    // Links between crash atom and condition of app is in syncing.
+    auto links = metric->add_links();
+    links->set_condition(isSyncingPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
+    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+        ConfigKey cfgKey;
+        auto config = CreateDurationMetricConfigWithLink(aggregationType);
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        std::vector<AttributionNode> attributions1 =
+            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        std::vector<AttributionNode> attributions2 =
+            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(555, "GMSCoreModule2")};
+
+        std::vector<std::unique_ptr<LogEvent>> events;
+
+        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
+        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
+
+        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
+        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
+
+        events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
+        events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
+
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 300));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+            bucketStartTimeNs + bucketSizeNs - 1));
+
+        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 700));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+
+        ConfigMetricsReportList reports;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        StatsLogReport::DurationMetricDataWrapper metrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+        EXPECT_EQ(metrics.data_size(), 3);
+        auto data = metrics.data(0);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+        EXPECT_FALSE(data.dimensions_in_condition().has_field());
+        EXPECT_EQ(data.bucket_info_size(), 1);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+        data = metrics.data(1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(2);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4504a95..233031c 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -44,9 +44,10 @@
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
 
     auto isSyncingPredicate = CreateIsSyncingPredicate();
-    *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
-        CreateDimensions(
-            android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/});
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field*/);
 
     auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
     *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
@@ -78,9 +79,8 @@
     auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    auto dimensionCondition = links->mutable_fields_in_condition();
-    dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
-    dimensionCondition->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
 
     // Links between crash atom and condition of app is in background.
     links = countMetric->add_links();
@@ -88,7 +88,7 @@
     dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    dimensionCondition = links->mutable_fields_in_condition();
+    auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     dimensionCondition->add_child()->set_field(1);  // uid field.
     return config;
@@ -132,12 +132,14 @@
         CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
                                       bucketStartTimeNs + 2 * bucketSizeNs - 100);
 
+    std::vector<AttributionNode> attributions =
+        {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
     auto syncOnEvent1 =
-        CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+        CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
     auto syncOffEvent1 =
-        CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+        CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
     auto syncOnEvent2 =
-        CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+        CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
 
     auto moveToBackgroundEvent1 =
         CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 4ad2097..50b3532 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -67,9 +67,9 @@
     // Flushes.
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets.size());
     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -80,10 +80,10 @@
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
-    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
     EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
     EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
     EXPECT_EQ(1LL, bucketInfo2.mCount);
@@ -91,9 +91,9 @@
     // nothing happens in bucket 3. we should not record anything for bucket 3.
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(2UL, buckets3.size());
 }
 
@@ -124,10 +124,10 @@
 
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
     {
-        const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+        const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
         EXPECT_EQ(1UL, buckets.size());
         const auto& bucketInfo = buckets[0];
         EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -167,9 +167,9 @@
         {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs);
@@ -181,9 +181,9 @@
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets.size());
     const auto& bucketInfo = buckets[0];
     EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -229,13 +229,13 @@
 
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // Two events in bucket #3.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -244,13 +244,13 @@
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
     // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index a59f1fe..c9fe252 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -62,9 +62,9 @@
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 durationProducer.mPastBuckets.end());
-    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(2UL, buckets.size());
     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -107,9 +107,9 @@
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 durationProducer.mPastBuckets.end());
-    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets2.size());
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index da00cae..3deab37 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -114,9 +114,9 @@
     key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
 
     EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 4533ac6..58be5b0 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -218,7 +218,7 @@
     EXPECT_EQ(13L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     std::shared_ptr<LogEvent> event2 =
             std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
@@ -231,7 +231,7 @@
     EXPECT_EQ(15L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     std::shared_ptr<LogEvent> event3 =
@@ -245,7 +245,7 @@
     EXPECT_EQ(26L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     // The event4 does not have the gauge field. Thus the current bucket value is 0.
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 0772b0d40..203f028 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -41,22 +41,24 @@
 
 const int TagId = 1;
 
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
-const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
-const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
     // Event starts again. This would not change anything as it already starts.
@@ -75,16 +77,22 @@
 }
 
 TEST(MaxDurationTrackerTest, TestStopAll) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
 
@@ -105,21 +113,26 @@
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     // The event starts.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
 
-    // Starts again. Does not change anything.
+    // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
                       ConditionKey());
 
@@ -135,16 +148,21 @@
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     // 2 starts
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -160,7 +178,8 @@
     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     // real stop now.
-    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY,
+                     bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
 
     EXPECT_EQ(3u, buckets[eventKey].size());
@@ -170,16 +189,20 @@
 }
 
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
-    HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
+    MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
     conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
 
-    EXPECT_CALL(*wizard, query(_, conditionKey1))  // #4
+    EXPECT_CALL(*wizard, query(_, conditionKey1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -187,8 +210,8 @@
     int64_t durationTimeNs = 2 * 1000;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
-                               bucketSizeNs, true, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, true, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -204,6 +227,10 @@
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+    FieldMatcher dimensionInCondition;
     int64_t metricId = 1;
     Alert alert;
     alert.set_id(101);
@@ -213,7 +240,7 @@
     const int32_t refPeriodSec = 1;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -221,8 +248,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, false, {anomalyTracker});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
 
     tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStop(key1, eventStartTimeNs + 10, false);
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6b8893e..80e16a1 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -38,24 +38,26 @@
 const ConfigKey kConfigKey(0, 12345);
 const int TagId = 1;
 const int64_t metricId = 123;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-
-const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -71,16 +73,23 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationNested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
@@ -95,16 +104,23 @@
 }
 
 TEST(OringDurationTrackerTest, TestStopAll) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
@@ -118,17 +134,24 @@
 }
 
 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -150,23 +173,30 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
 
@@ -181,25 +211,32 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))
+    EXPECT_CALL(*wizard, query(_, key1, _, _))
             .Times(2)
             .WillOnce(Return(ConditionState::kFalse))
             .WillOnce(Return(ConditionState::kTrue));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -216,22 +253,29 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -249,6 +293,13 @@
 }
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -256,7 +307,7 @@
     alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -264,8 +315,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, true, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker});
 
     // Nothing in the past bucket.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -310,6 +361,12 @@
 }
 
 TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -318,7 +375,7 @@
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -326,8 +383,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -352,6 +409,13 @@
 }
 
 TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -360,7 +424,7 @@
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey conkey;
     conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
@@ -369,8 +433,9 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false,
+                                 {anomalyTracker});
 
     tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index fff3dbf..55c078d 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -299,26 +299,26 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
     // Value sum == 30 <= 130.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
     // Value sum == 130 <= 130.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // Three events in bucket #3.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
     // Anomaly at event 4 since Value sum == 131 > 130!
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
     // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
     // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index fc7245c..ab9345a 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -26,6 +26,13 @@
     return HashableDimensionKey(dimensionsValue);
 }
 
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
+    DimensionsValue dimensionsValue;
+    dimensionsValue.set_field(tagId);
+    dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+    dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+    return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 23e86f9..0a97456 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -25,10 +25,12 @@
 
 class MockConditionWizard : public ConditionWizard {
 public:
-    MOCK_METHOD2(
+    MOCK_METHOD4(
             query,
             ConditionState(const int conditionIndex,
-                           const ConditionKey& conditionParameters));
+                           const ConditionKey& conditionParameters,
+                           const FieldMatcher& dimensionFields,
+                           std::unordered_set<HashableDimensionKey> *dimensionKeySet));
 };
 
 class MockStatsPullerManager : public StatsPullerManager {
@@ -39,6 +41,7 @@
 };
 
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 9f4582d..13055cb 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gtest/gtest.h>
 #include "statsd_test_util.h"
 
 namespace android {
@@ -27,6 +26,22 @@
     return atom_matcher;
 }
 
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
+    return atom_matcher;
+}
+
+AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId("UidProcessStateChanged"));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
+    return atom_matcher;
+}
+
 AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
                                                   WakelockStateChanged::State state) {
     AtomMatcher atom_matcher;
@@ -47,6 +62,30 @@
     return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
 }
 
+AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
+    const string& name, BatterySaverModeStateChanged::State state) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId(name));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(1);  // State field.
+    field_value_matcher->set_eq_int(state);
+    return atom_matcher;
+}
+
+AtomMatcher CreateBatterySaverModeStartAtomMatcher() {
+    return CreateBatterySaverModeStateChangedAtomMatcher(
+        "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
+}
+
+
+AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
+    return CreateBatterySaverModeStateChangedAtomMatcher(
+        "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
+}
+
+
 AtomMatcher CreateScreenStateChangedAtomMatcher(
     const string& name, android::view::DisplayStateEnum state) {
     AtomMatcher atom_matcher;
@@ -59,6 +98,7 @@
     return atom_matcher;
 }
 
+
 AtomMatcher CreateScreenTurnedOnAtomMatcher() {
     return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
             android::view::DisplayStateEnum::DISPLAY_STATE_ON);
@@ -128,6 +168,13 @@
         "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
 }
 
+Predicate CreateBatterySaverModePredicate() {
+    Predicate predicate;
+    predicate.set_id(StringToId("BatterySaverIsOn"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
+    return predicate;
+}
 
 Predicate CreateScreenIsOnPredicate() {
     Predicate predicate;
@@ -218,6 +265,31 @@
     return event;
 }
 
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(
+        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(
+        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+    int level, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(level));
+    event->init();
+    return event;
+
+}
+
 std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
     const std::vector<AttributionNode>& attributions, const string& wakelockName,
     const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -267,9 +339,10 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
-    const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+    const std::vector<AttributionNode>& attributions,
+    const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
-    event->write(uid);
+    event->write(attributions);
     event->write(name);
     event->write(state);
     event->init();
@@ -277,13 +350,13 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const int uid, const string& name, uint64_t timestampNs){
-    return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncEndEvent(
-    const int uid, const string& name, uint64_t timestampNs) {
-    return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ff8fef0c..6638893 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,6 +14,7 @@
 
 #pragma once
 
+#include <gtest/gtest.h>
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "statslog.h"
 #include "src/logd/LogEvent.h"
@@ -26,6 +27,18 @@
 // Create AtomMatcher proto to simply match a specific atom type.
 AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
 
+// Create AtomMatcher proto for screen brightness state changed.
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
+
+// Create AtomMatcher proto for starting battery save mode.
+AtomMatcher CreateBatterySaverModeStartAtomMatcher();
+
+// Create AtomMatcher proto for stopping battery save mode.
+AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+
+// Create AtomMatcher proto for process state changed.
+AtomMatcher CreateUidProcessStateChangedAtomMatcher();
+
 // Create AtomMatcher proto for acquiring wakelock.
 AtomMatcher CreateAcquireWakelockAtomMatcher();
 
@@ -59,6 +72,9 @@
 // Create Predicate proto for screen is off.
 Predicate CreateScreenIsOffPredicate();
 
+// Create Predicate proto for battery saver mode.
+Predicate CreateBatterySaverModePredicate();
+
 // Create Predicate proto for holding wakelock.
 Predicate CreateHoldingWakelockPredicate();
 
@@ -86,6 +102,15 @@
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
     const android::view::DisplayStateEnum state, uint64_t timestampNs);
 
+// Create log event for screen brightness state changed.
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+   int level, uint64_t timestampNs);
+
+// Create log event when battery saver starts.
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
+// Create log event when battery saver stops.
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+
 // Create log event for app moving to background.
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
 
@@ -94,11 +119,11 @@
 
 // Create log event when the app sync starts.
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const int uid, const string& name, uint64_t timestampNs);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
 
 // Create log event when the app sync ends.
 std::unique_ptr<LogEvent> CreateSyncEndEvent(
-    const int uid, const string& name, uint64_t timestampNs);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
 
 // Create log event when the app sync ends.
 std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -136,9 +161,12 @@
 
 template <typename T>
 void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
-    std::map<HashableDimensionKey, int> dimensionIndexMap;
+    std::map<MetricDimensionKey, int> dimensionIndexMap;
     for (int i = 0; i < metricData.data_size(); ++i) {
-        dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
+        dimensionIndexMap.insert(std::make_pair(
+            MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
+            HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
+            i));
     }
     for (const auto& itr : dimensionIndexMap) {
         *sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index 32a85b1..a65095f 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_DEX_PREOPT := false
+LOCAL_CERTIFICATE := platform
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
index 7bfde40..cd76c9d 100644
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml
@@ -18,6 +18,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.statsd.dogfood"
+    android:sharedUserId="android.uid.system"
     android:versionCode="1"
     android:versionName="1.0" >
 
diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
index c1c3914..d050061 100644
--- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
+++ b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index d39aa1d..57575ae 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -116,28 +116,32 @@
         findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1);
+                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+                        StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
             }
         });
 
         findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0);
+                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+                        StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
             }
         });
 
         findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2);
+                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+                        StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
             }
         });
 
         findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1);
+                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+                        StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
             }
         });
 
@@ -255,7 +259,9 @@
         }
         int[] uids = new int[] {mUids[id]};
         String[] tags  = new String[] {"acquire"};
-        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
+        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+                StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+                StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
         StringBuilder sb = new StringBuilder();
         sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
                 .append(", ").append(name).append(", 1);");
@@ -269,7 +275,9 @@
         }
         int[] uids = new int[] {mUids[id]};
         String[] tags  = new String[] {"release"};
-        StatsLog.write(10, uids, tags, 0, name, 0);
+        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+                StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+                StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
         StringBuilder sb = new StringBuilder();
         sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
                 .append(", ").append(name).append(", 0);");
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 34f6d7d..3893be4 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -21,7 +21,6 @@
 import android.hardware.usb.UsbManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 
 public class UsbCommand extends Svc.Command {
     public UsbCommand() {
@@ -37,41 +36,41 @@
     public String longHelp() {
         return shortHelp() + "\n"
                 + "\n"
-                + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n"
-                + "         Set the current usb function and optionally the data lock state.\n\n"
+                + "usage: svc usb setFunctions [function]\n"
+                + "         Set the current usb function. If function is blank, sets to charging.\n"
                 + "       svc usb setScreenUnlockedFunctions [function]\n"
-                + "         Sets the functions which, if the device was charging,"
-                    + " become current on screen unlock.\n"
-                + "       svc usb getFunction\n"
-                + "          Gets the list of currently enabled functions\n";
+                + "         Sets the functions which, if the device was charging, become current on"
+                    + "screen unlock. If function is blank, turn off this feature.\n"
+                + "       svc usb getFunctions\n"
+                + "          Gets the list of currently enabled functions\n\n"
+                + "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n";
     }
 
     @Override
     public void run(String[] args) {
-        boolean validCommand = false;
         if (args.length >= 2) {
-            if ("setFunction".equals(args[1])) {
-                IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
-                        Context.USB_SERVICE));
-                boolean unlockData = false;
-                if (args.length >= 4) {
-                    unlockData = Boolean.valueOf(args[3]);
-                }
+            IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
+                    Context.USB_SERVICE));
+            if ("setFunctions".equals(args[1])) {
                 try {
-                    usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), unlockData);
+                    usbMgr.setCurrentFunctions(UsbManager.usbFunctionsFromString(
+                            args.length >= 3 ? args[2] : ""));
                 } catch (RemoteException e) {
                     System.err.println("Error communicating with UsbManager: " + e);
                 }
                 return;
-            } else if ("getFunction".equals(args[1])) {
-                System.err.println(SystemProperties.get("sys.usb.config"));
+            } else if ("getFunctions".equals(args[1])) {
+                try {
+                    System.err.println(
+                            UsbManager.usbFunctionsToString(usbMgr.getCurrentFunctions()));
+                } catch (RemoteException e) {
+                    System.err.println("Error communicating with UsbManager: " + e);
+                }
                 return;
             } else if ("setScreenUnlockedFunctions".equals(args[1])) {
-                IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
-                        Context.USB_SERVICE));
                 try {
-                    usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] :
-                            UsbManager.USB_FUNCTION_NONE));
+                    usbMgr.setScreenUnlockedFunctions(UsbManager.usbFunctionsFromString(
+                            args.length >= 3 ? args[2] : ""));
                 } catch (RemoteException e) {
                     System.err.println("Error communicating with UsbManager: " + e);
                 }
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
new file mode 100644
index 0000000..05e51a4
--- /dev/null
+++ b/config/hiddenapi-light-greylist.txt
@@ -0,0 +1,1105 @@
+Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/animation/ValueAnimator;->animateValue(F)V
+Landroid/animation/ValueAnimator;->getDurationScale()F
+Landroid/animation/ValueAnimator;->sDurationScale:F
+Landroid/app/Activity;->convertFromTranslucent()V
+Landroid/app/Activity;->convertToTranslucent(Landroid/app/Activity$TranslucentConversionListener;Landroid/app/ActivityOptions;)Z
+Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
+Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z
+Landroid/app/ActivityManager;->forceStopPackage(Ljava/lang/String;)V
+Landroid/app/ActivityManager;->getCurrentUser()I
+Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z
+Landroid/app/ActivityManager;->isUserRunning(I)Z
+Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
+Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
+Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
+Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
+Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
+Landroid/app/Activity;->mApplication:Landroid/app/Application;
+Landroid/app/Activity;->mHandler:Landroid/os/Handler;
+Landroid/app/Activity;->mResultCode:I
+Landroid/app/Activity;->mResultData:Landroid/content/Intent;
+Landroid/app/Activity;->mToken:Landroid/os/IBinder;
+Landroid/app/Activity;->mWindow:Landroid/view/Window;
+Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder;
+Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
+Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread;
+Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String;
+Landroid/app/ActivityThread;->getApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread;
+Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler;
+Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread$H;->BIND_SERVICE:I
+Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
+Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
+Landroid/app/ActivityThread$H;->RECEIVER:I
+Landroid/app/ActivityThread$H;->RELAUNCH_ACTIVITY:I
+Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
+Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
+Landroid/app/ActivityThread$H;->STOP_SERVICE:I
+Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I
+Landroid/app/ActivityThread;->installContentProviders(Landroid/content/Context;Ljava/util/List;)V
+Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
+Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
+Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation;
+Landroid/app/ActivityThread;->mNumVisibleActivities:I
+Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
+Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List;
+Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(J)V
+Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(JZ)V
+Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z
+Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;)Z
+Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V
+Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;Z)V
+Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
+Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
+Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
+Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/Application;->attach(Landroid/content/Context;)V
+Landroid/app/ApplicationLoaders;->getDefault()Landroid/app/ApplicationLoaders;
+Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap;
+Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList;
+Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk;
+Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->noteOp(I)I
+Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
+Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
+Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
+Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V
+Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/BackupDataInputStream;->dataSize:I
+Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String;
+Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I
+Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl;
+Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
+Landroid/app/ContextImpl;->mPackageInfo:Landroid/app/LoadedApk;
+Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
+Landroid/app/DatePickerDialog;->mDatePicker:Landroid/widget/DatePicker;
+Landroid/app/Dialog;->CANCEL:I
+Landroid/app/Dialog;->mCancelMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mDismissMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler;
+Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity;
+Landroid/app/Dialog;->mShowMessage:Landroid/os/Message;
+Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
+Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
+Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
+Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
+Landroid/app/IActivityManager;->resumeAppSwitches()V
+Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
+Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
+Landroid/app/LoadedApk;->mApplication:Landroid/app/Application;
+Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap;
+Landroid/app/LoadedApk;->mResDir:Ljava/lang/String;
+Landroid/app/LoadedApk;->mResources:Landroid/content/res/Resources;
+Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map;
+Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList;
+Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
+Landroid/app/Notification;->EXTRA_SUBSTITUTE_APP_NAME:Ljava/lang/String;
+Landroid/app/Notification;->isGroupSummary()Z
+Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
+Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V
+Landroid/app/Notification;->setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
+Landroid/app/PendingIntent;->getActivityAsUser(Landroid/content/Context;ILandroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/PendingIntent;
+Landroid/app/PendingIntent;->getIntent()Landroid/content/Intent;
+Landroid/app/PendingIntent;->isActivity()Z
+Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
+Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
+Landroid/app/Service;->setForeground(Z)V
+Landroid/app/StatusBarManager;->collapsePanels()V
+Landroid/app/StatusBarManager;->disable(I)V
+Landroid/app/StatusBarManager;->expandNotificationsPanel()V
+Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V
+Landroid/app/StatusBarManager;->expandSettingsPanel()V
+Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
+Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getIWallpaperManager()Landroid/app/IWallpaperManager;
+Landroid/app/WallpaperManager;->openDefaultWallpaper(Landroid/content/Context;I)Ljava/io/InputStream;
+Landroid/app/WallpaperManager;->setBitmap(Landroid/graphics/Bitmap;Landroid/graphics/Rect;ZII)I
+Landroid/app/WallpaperManager;->sGlobals:Landroid/app/WallpaperManager$Globals;
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetIdIfAllowed(IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V
+Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
+Landroid/bluetooth/BluetoothAdapter;->enableNoAutoConnect()Z
+Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I
+Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
+Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
+Landroid/bluetooth/BluetoothDevice;->cancelBondProcess()Z
+Landroid/bluetooth/BluetoothDevice;->createBond(I)Z
+Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String;
+Landroid/bluetooth/BluetoothDevice;->isConnected()Z
+Landroid/bluetooth/BluetoothDevice;->isEncrypted()Z
+Landroid/bluetooth/BluetoothDevice;->removeBond()Z
+Landroid/bluetooth/BluetoothDevice;->setPhonebookAccessPermission(I)Z
+Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I
+Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService;
+Landroid/bluetooth/BluetoothGattDescriptor;->mCharacteristic:Landroid/bluetooth/BluetoothGattCharacteristic;
+Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I
+Landroid/bluetooth/BluetoothGatt;->refresh()Z
+Landroid/bluetooth/BluetoothHeadset;->close()V
+Landroid/bluetooth/BluetoothHeadset;->setPriority(Landroid/bluetooth/BluetoothDevice;I)Z
+Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
+Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
+Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
+Landroid/content/ContentProviderOperation;->mSelection:Ljava/lang/String;
+Landroid/content/ContentProviderOperation;->mType:I
+Landroid/content/ContentResolver;->getContentService()Landroid/content/IContentService;
+Landroid/content/ContentResolver;->getSyncStatus(Landroid/accounts/Account;Ljava/lang/String;)Landroid/content/SyncStatusInfo;
+Landroid/content/ContentResolver;->mContext:Landroid/content/Context;
+Landroid/content/ContentValues;->mValues:Ljava/util/HashMap;
+Landroid/content/Context;->createPackageContextAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/Context;
+Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File;
+Landroid/content/Context;->getThemeResId()I
+Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;I)V
+Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/ContextWrapper;->mBase:Landroid/content/Context;
+Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal;
+Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver;
+Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V
+Landroid/content/IContentService;->getMasterSyncAutomatically()Z
+Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V
+Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
+Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
+Landroid/content/pm/ApplicationInfo;->installLocation:I
+Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z
+Landroid/content/pm/ApplicationInfo;->isPrivilegedApp()Z
+Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
+Landroid/content/pm/ApplicationInfo;->privateFlags:I
+Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
+Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
+Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
+Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
+Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->freeStorage(Ljava/lang/String;JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->getPackageCandidateVolumes(Landroid/content/pm/ApplicationInfo;)Ljava/util/List;
+Landroid/content/pm/PackageManager;->getPackageSizeInfo(Ljava/lang/String;Landroid/content/pm/IPackageStatsObserver;)V
+Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources;
+Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I
+Landroid/content/pm/PackageManager;->queryBroadcastReceivers(Landroid/content/Intent;II)Ljava/util/List;
+Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
+Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/UserInfo;->id:I
+Landroid/content/pm/UserInfo;->isPrimary()Z
+Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
+Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
+Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V
+Landroid/content/res/AssetManager;->getArraySize(I)I
+Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
+Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String;
+Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
+Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;
+Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J
+Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J
+Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
+Landroid/content/res/AssetManager;->retrieveArray(I[I)I
+Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z
+Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I
+Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I
+Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
+Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
+Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
+Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration;
+Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mPreloading:Z
+Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_MISS_PRELOAD:Z
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_PRELOAD:Z
+Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser;
+Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser;
+Landroid/content/res/Resources;->mResourcesImpl:Landroid/content/res/ResourcesImpl;
+Landroid/content/res/Resources;->mTmpValue:Landroid/util/TypedValue;
+Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$SynchronizedPool;
+Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
+Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z
+Landroid/content/res/TypedArray;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/TypedArray;->mData:[I
+Landroid/content/res/TypedArray;->mIndices:[I
+Landroid/content/res/TypedArray;->mLength:I
+Landroid/content/res/TypedArray;->mMetrics:Landroid/util/DisplayMetrics;
+Landroid/content/res/TypedArray;->mRecycled:Z
+Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources;
+Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue;
+Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser;
+Landroid/content/res/XmlBlock;->close()V
+Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser;
+Landroid/content/res/XmlBlock$Parser;->mParseState:J
+Landroid/content/SyncStatusInfo;->lastSuccessTime:J
+Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
+Landroid/database/CursorWindow;->mWindowPtr:J
+Landroid/database/CursorWindow;->sCursorWindowSize:I
+Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
+Landroid/database/CursorWrapper;->mCursor:Landroid/database/Cursor;
+Landroid/graphics/Bitmap$Config;->nativeInt:I
+Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->getDefaultDensity()I
+Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I
+Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList;
+Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
+Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
+Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
+Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
+Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
+Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
+Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
+Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
+Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
+Landroid/graphics/drawable/StateListDrawable;->getStateCount()I
+Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable;
+Landroid/graphics/drawable/StateListDrawable;->getStateSet(I)[I
+Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState;
+Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V
+Landroid/graphics/LinearGradient;->mColors:[I
+Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
+Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I
+Landroid/graphics/Typeface;->mStyle:I
+Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
+Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
+Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
+Landroid/hardware/Camera;->addCallbackBuffer([BI)V
+Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
+Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
+Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager;
+Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager;
+Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V
+Landroid/location/LocationRequest;->createFromDeprecatedProvider(Ljava/lang/String;JFZ)Landroid/location/LocationRequest;
+Landroid/location/LocationRequest;->setWorkSource(Landroid/os/WorkSource;)V
+Landroid/location/Location;->setIsFromMockProvider(Z)V
+Landroid/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder;
+Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I
+Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap;
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioFocusRequest;Landroid/media/audiopolicy/AudioPolicy;)I
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;II)I
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;IILandroid/media/audiopolicy/AudioPolicy;)I
+Landroid/media/AudioManager;->setMasterMute(ZI)V
+Landroid/media/AudioManager;->STREAM_BLUETOOTH_SCO:I
+Landroid/media/AudioManager;->STREAM_SYSTEM_ENFORCED:I
+Landroid/media/AudioManager;->STREAM_TTS:I
+Landroid/media/AudioSystem;->setDeviceConnectionState(IILjava/lang/String;Ljava/lang/String;)I
+Landroid/media/AudioTrack;->getLatency()I
+Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
+Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V
+Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType;
+Landroid/media/MediaFile;->getMimeTypeForFile(Ljava/lang/String;)Ljava/lang/String;
+Landroid/media/MediaFile;->isVideoFileType(I)Z
+Landroid/media/MediaFile$MediaFileType;->fileType:I
+Landroid/media/MediaFile;->sFileTypeMap:Ljava/util/HashMap;
+Landroid/media/MediaMetadataRetriever;->getEmbeddedPicture(I)[B
+Landroid/media/MediaPlayer;->getMetadata(ZZ)Landroid/media/Metadata;
+Landroid/media/MediaPlayer;->invoke(Landroid/os/Parcel;Landroid/os/Parcel;)V
+Landroid/media/MediaPlayer;->newRequest()Landroid/os/Parcel;
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;)V
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V
+Landroid/media/MediaPlayer;->setRetransmitEndpoint(Ljava/net/InetSocketAddress;)V
+Landroid/media/MediaRecorder$AudioSource;->RADIO_TUNER:I
+Landroid/media/MediaRouter$RouteInfo;->getStatusCode()I
+Landroid/media/MediaRouter$RouteInfo;->STATUS_CONNECTING:I
+Landroid/media/MediaRouter;->selectRouteInt(ILandroid/media/MediaRouter$RouteInfo;Z)V
+Landroid/media/MediaScanner;->mClient:Landroid/media/MediaScanner$MyMediaScannerClient;
+Landroid/media/MediaScanner;->scanSingleFile(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;
+Landroid/media/MiniThumbFile;->reset()V
+Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
+Landroid/media/Ringtone;->setLooping(Z)V
+Landroid/media/Ringtone;->setVolume(F)V
+Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
+Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
+Landroid/net/ConnectivityManager;->getLinkProperties(I)Landroid/net/LinkProperties;
+Landroid/net/ConnectivityManager;->getMobileDataEnabled()Z
+Landroid/net/ConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->isNetworkSupported(I)Z
+Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z
+Landroid/net/ConnectivityManager;->isTetheringSupported()Z
+Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager;
+Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z
+Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z
+Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
+Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory;
+Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
+Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
+Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/WifiConfiguration;->apBand:I
+Landroid/net/wifi/WifiConfiguration;->apChannel:I
+Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z
+Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
+Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
+Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;
+Landroid/net/wifi/WifiManager;->forget(ILandroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;
+Landroid/net/wifi/WifiManager;->getWifiApState()I
+Landroid/net/wifi/WifiManager;->isDualBandSupported()Z
+Landroid/net/wifi/WifiManager;->isWifiApEnabled()Z
+Landroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager;
+Landroid/net/wifi/WifiManager;->save(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I
+Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
+Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext;
+Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper;
+Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread;
+Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
+Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
+Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
+Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable;
+Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
+Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
+Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
+Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
+Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
+Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray;
+Landroid/os/BatteryStats$Uid;->getUid()I
+Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
+Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
+Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
+Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
+Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
+Landroid/os/Debug;->countInstancesOfClass(Ljava/lang/Class;)J
+Landroid/os/Debug;->dumpReferenceTables()V
+Landroid/os/Debug$MemoryInfo;->getOtherLabel(I)Ljava/lang/String;
+Landroid/os/Debug$MemoryInfo;->getOtherPrivateDirty(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherPss(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherSharedDirty(I)I
+Landroid/os/Debug$MemoryInfo;->NUM_DVK_STATS:I
+Landroid/os/Debug$MemoryInfo;->NUM_OTHER_STATS:I
+Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J
+Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z
+Landroid/os/FileUtils;->copyToFile(Ljava/io/InputStream;Ljava/io/File;)Z
+Landroid/os/FileUtils;->deleteOlderFiles(Ljava/io/File;IJ)Z
+Landroid/os/FileUtils;->readTextFile(Ljava/io/File;ILjava/lang/String;)Ljava/lang/String;
+Landroid/os/FileUtils;->setPermissions(Ljava/io/FileDescriptor;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/io/File;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I
+Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
+Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z
+Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
+Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
+Landroid/os/IPowerManager;->userActivity(JII)V
+Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue;
+Landroid/os/MemoryFile;->getFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Message;->callback:Ljava/lang/Runnable;
+Landroid/os/Message;->flags:I
+Landroid/os/Message;->next:Landroid/os/Message;
+Landroid/os/MessageQueue;->mIdleHandlers:Ljava/util/ArrayList;
+Landroid/os/MessageQueue;->mMessages:Landroid/os/Message;
+Landroid/os/MessageQueue;->mQuitAllowed:Z
+Landroid/os/MessageQueue;->next()Landroid/os/Message;
+Landroid/os/Message;->target:Landroid/os/Handler;
+Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
+Landroid/os/PowerManager;->userActivity(JII)V
+Landroid/os/PowerManager;->userActivity(JZ)V
+Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
+Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
+Landroid/os/PowerManager;->wakeUp(J)V
+Landroid/os/Process;->getParentPid(I)I
+Landroid/os/Process;->getPids(Ljava/lang/String;[I)[I
+Landroid/os/Process;->getUidForPid(I)I
+Landroid/os/Process;->isIsolated(I)Z
+Landroid/os/Process;->isIsolated()Z
+Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
+Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
+Landroid/os/RecoverySystem;->cancelScheduledUpdate(Landroid/content/Context;)V
+Landroid/os/RecoverySystem;->installPackage(Landroid/content/Context;Ljava/io/File;Z)V
+Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;Landroid/os/Handler;)V
+Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;)V
+Landroid/os/RecoverySystem;->rebootWipeAb(Landroid/content/Context;Ljava/io/File;Ljava/lang/String;)V
+Landroid/os/RecoverySystem;->scheduleUpdateOnBoot(Landroid/content/Context;Ljava/io/File;)V
+Landroid/os/SELinux;->isSELinuxEnabled()Z
+Landroid/os/SELinux;->isSELinuxEnforced()Z
+Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/ServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/storage/DiskInfo;->getDescription()Ljava/lang/String;
+Landroid/os/storage/StorageManager;->findVolumeByUuid(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
+Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J
+Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumePaths()[Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getVolumes()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getVolumeState(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File;
+Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getDisk()Landroid/os/storage/DiskInfo;
+Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
+Landroid/os/storage/VolumeInfo;->getState()I
+Landroid/os/storage/VolumeInfo;->getType()I
+Landroid/os/storage/VolumeInfo;->isPrimary()Z
+Landroid/os/storage/VolumeInfo;->isVisible()Z
+Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
+Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V
+Landroid/os/SystemProperties;->getBoolean(Ljava/lang/String;Z)Z
+Landroid/os/SystemProperties;->getInt(Ljava/lang/String;I)I
+Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/SystemProperties;->getLong(Ljava/lang/String;J)J
+Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
+Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
+Landroid/os/Trace;->isTagEnabled(J)Z
+Landroid/os/Trace;->setAppTracingAllowed(Z)V
+Landroid/os/Trace;->traceBegin(JLjava/lang/String;)V
+Landroid/os/Trace;->traceCounter(JLjava/lang/String;I)V
+Landroid/os/Trace;->traceEnd(J)V
+Landroid/os/Trace;->TRACE_TAG_APP:J
+Landroid/os/Trace;->TRACE_TAG_VIEW:J
+Landroid/os/UpdateEngine;->applyPayload(Ljava/lang/String;JJ[Ljava/lang/String;)V
+Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;Landroid/os/Handler;)Z
+Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;)Z
+Landroid/os/UpdateEngine;->cancel()V
+Landroid/os/UpdateEngine;->resetStatus()V
+Landroid/os/UpdateLock;->acquire()V
+Landroid/os/UpdateLock;->isHeld()Z
+Landroid/os/UpdateLock;->release()V
+Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle;
+Landroid/os/UserHandle;->getIdentifier()I
+Landroid/os/UserHandle;->getUserId(I)I
+Landroid/os/UserHandle;->isOwner()Z
+Landroid/os/UserHandle;->myUserId()I
+Landroid/os/UserHandle;->of(I)Landroid/os/UserHandle;
+Landroid/os/UserManager;->get(Landroid/content/Context;)Landroid/os/UserManager;
+Landroid/os/UserManager;->getMaxSupportedUsers()I
+Landroid/os/UserManager;->getProfiles(I)Ljava/util/List;
+Landroid/os/UserManager;->getUserHandle()I
+Landroid/os/UserManager;->getUserHandle(I)I
+Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo;
+Landroid/os/UserManager;->getUserSerialNumber(I)I
+Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isUserUnlocked(I)Z
+Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
+Landroid/os/WorkSource;->add(I)Z
+Landroid/os/WorkSource;->get(I)I
+Landroid/os/WorkSource;->getName(I)Ljava/lang/String;
+Landroid/os/WorkSource;->size()I
+Landroid/preference/DialogPreference;->mBuilder:Landroid/app/AlertDialog$Builder;
+Landroid/preference/DialogPreference;->mDialogIcon:Landroid/graphics/drawable/Drawable;
+Landroid/preference/DialogPreference;->mDialog:Landroid/app/Dialog;
+Landroid/preference/DialogPreference;->mDialogMessage:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mDialogTitle:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mNegativeButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mPositiveButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mWhichButtonClicked:I
+Landroid/preference/ListPreference;->mClickedDialogEntryIndex:I
+Landroid/preference/PreferenceActivity;->mPreferenceManager:Landroid/preference/PreferenceManager;
+Landroid/preference/PreferenceActivity;->mPrefsContainer:Landroid/view/ViewGroup;
+Landroid/preference/PreferenceManager;->dispatchActivityDestroy()V
+Landroid/preference/PreferenceManager;->dispatchActivityResult(IILandroid/content/Intent;)V
+Landroid/preference/PreferenceManager;->dispatchActivityStop()V
+Landroid/preference/PreferenceManager;->getEditor()Landroid/content/SharedPreferences$Editor;
+Landroid/preference/PreferenceManager;->getPreferenceScreen()Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->inflateFromIntent(Landroid/content/Intent;Landroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->inflateFromResource(Landroid/content/Context;ILandroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->mActivityDestroyListeners:Ljava/util/List;
+Landroid/preference/PreferenceManager;->mOnPreferenceTreeClickListener:Landroid/preference/PreferenceManager$OnPreferenceTreeClickListener;
+Landroid/preference/PreferenceManager;->mSharedPreferences:Landroid/content/SharedPreferences;
+Landroid/preference/PreferenceManager;->registerOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->registerOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/PreferenceManager;->setFragment(Landroid/preference/PreferenceFragment;)V
+Landroid/preference/PreferenceManager;->setPreferences(Landroid/preference/PreferenceScreen;)Z
+Landroid/preference/PreferenceManager;->shouldCommit()Z
+Landroid/preference/PreferenceManager;->unregisterOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->unregisterOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/Preference;->onKey(Landroid/view/View;ILandroid/view/KeyEvent;)Z
+Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V
+Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter;
+Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
+Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
+Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
+Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z
+Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
+Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/provider/Settings$Global;->OTA_DISABLE_AUTOMATIC_UPDATE:Ljava/lang/String;
+Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->PACKAGE_VERIFIER_USER_CONSENT:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->USER_SETUP_COMPLETE:Ljava/lang/String;
+Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String;
+Landroid/provider/Settings$System;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Settings$System;->putStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;I)Z
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/renderscript/RenderScript;->create(Landroid/content/Context;I)Landroid/renderscript/RenderScript;
+Landroid/renderscript/RenderScript;->create(Landroid/content/Context;ILandroid/renderscript/RenderScript$ContextType;I)Landroid/renderscript/RenderScript;
+Landroid/renderscript/RenderScript;->getMinorID()J
+Landroid/R$styleable;->TextAppearance:[I
+Landroid/R$styleable;->TextAppearance_textColor:I
+Landroid/R$styleable;->TextAppearance_textSize:I
+Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V
+Landroid/service/media/MediaBrowserService;->KEY_MEDIA_ITEM:Ljava/lang/String;
+Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V
+Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
+Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
+Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
+Landroid/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer;
+Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
+Landroid/telephony/SignalStrength;->getCdmaLevel()I
+Landroid/telephony/SignalStrength;->getLteDbm()I
+Landroid/telephony/SignalStrength;->getLteRsrp()I
+Landroid/telephony/SignalStrength;->getLteRssnr()I
+Landroid/telephony/SignalStrength;->getLteSignalStrength()I
+Landroid/telephony/SmsManager;->RESULT_ERROR_FDN_CHECK_FAILURE:I
+Landroid/telephony/SmsMessage;->getSubId()I
+Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase;
+Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I
+Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
+Landroid/telephony/SubscriptionManager;->getSubId(I)[I
+Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
+Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I
+Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I
+Landroid/telephony/TelephonyManager;->getDataEnabled(I)Z
+Landroid/telephony/TelephonyManager;->getDataEnabled()Z
+Landroid/telephony/TelephonyManager;->getDefault()Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getITelephony()Lcom/android/internal/telephony/ITelephony;
+Landroid/telephony/TelephonyManager;->getLine1Number(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkClass(I)I
+Landroid/telephony/TelephonyManager;->getNetworkOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkOperatorName(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkType(I)I
+Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
+Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V
+Landroid/text/AndroidBidi;->bidi(I[C[B)I
+Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
+Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
+Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
+Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
+Landroid/text/SpannableStringBuilder;->mGapLength:I
+Landroid/text/SpannableStringBuilder;->mGapStart:I
+Landroid/text/SpannableStringBuilder;->mSpanCount:I
+Landroid/text/SpannableStringBuilder;->mSpanEnds:[I
+Landroid/text/SpannableStringBuilder;->mSpanFlags:[I
+Landroid/text/SpannableStringBuilder;->mSpans:[Ljava/lang/Object;
+Landroid/text/SpannableStringBuilder;->mSpanStarts:[I
+Landroid/text/StaticLayout;->mColumns:I
+Landroid/text/StaticLayout;->mLineCount:I
+Landroid/text/StaticLayout;->mLines:[I
+Landroid/text/TextLine;->obtain()Landroid/text/TextLine;
+Landroid/text/TextLine;->sCached:[Landroid/text/TextLine;
+Landroid/text/TextPaint;->setUnderlineText(IF)V
+Landroid/util/Log;->wtf(ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ZZ)I
+Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object;
+Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z
+Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityManager;->sInstanceSync:Ljava/lang/Object;
+Landroid/view/accessibility/AccessibilityNodeInfo;->refresh(Landroid/os/Bundle;Z)Z
+Landroid/view/ActionMode;->isUiFocusable()Z
+Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
+Landroid/view/Choreographer;->doFrame(JI)V
+Landroid/view/Choreographer;->getFrameTime()J
+Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue;
+Landroid/view/Choreographer;->postCallback(ILjava/lang/Runnable;Ljava/lang/Object;)V
+Landroid/view/Choreographer;->removeCallbacks(ILjava/lang/Runnable;Ljava/lang/Object;)V
+Landroid/view/Choreographer;->scheduleVsyncLocked()V
+Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
+Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/view/ContextThemeWrapper;->mThemeResource:I
+Landroid/view/InputDevice;->isExternal()Z
+Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
+Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
+Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
+Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
+Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
+Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
+Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
+Landroid/view/IWindowManager;->getAnimationScale(I)F
+Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->setAnimationScale(IF)V
+Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
+Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/LayoutInflater;->mConstructorArgs:[Ljava/lang/Object;
+Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2;
+Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory;
+Landroid/view/LayoutInflater;->mFactorySet:Z
+Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
+Landroid/view/MotionEvent;->HISTORY_CURRENT:I
+Landroid/view/MotionEvent;->mNativePtr:J
+Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
+Landroid/view/MotionEvent;->scale(F)V
+Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
+Landroid/view/SurfaceView;->mCallbacks:Ljava/util/ArrayList;
+Landroid/view/SurfaceView;->mFormat:I
+Landroid/view/SurfaceView;->mRequestedFormat:I
+Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
+Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
+Landroid/view/TextureView;->mLayer:Landroid/view/HardwareLayer;
+Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
+Landroid/view/TextureView;->mUpdateListener:Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;
+Landroid/view/TouchDelegate;->mDelegateTargeted:Z
+Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
+Landroid/view/View;->clearAccessibilityFocus()V
+Landroid/view/View;->computeOpaqueFlags()V
+Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKey:Z
+Landroid/view/View;->createSnapshot(Landroid/view/ViewDebug$CanvasProvider;Z)Landroid/graphics/Bitmap;
+Landroid/view/ViewDebug;->dispatchCommand(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Ljava/io/OutputStream;)V
+Landroid/view/ViewDebug;->dump(Landroid/view/View;ZZLjava/io/OutputStream;)V
+Landroid/view/View;->dispatchAttachedToWindow(Landroid/view/View$AttachInfo;I)V
+Landroid/view/View;->dispatchDetachedFromWindow()V
+Landroid/view/View;->fitsSystemWindows()Z
+Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
+Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
+Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
+Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I
+Landroid/view/ViewGroup$MarginLayoutParams;->endMargin:I
+Landroid/view/ViewGroup$MarginLayoutParams;->startMargin:I
+Landroid/view/ViewGroup;->mChildren:[Landroid/view/View;
+Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
+Landroid/view/ViewGroup;->mGroupFlags:I
+Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
+Landroid/view/View;->isPaddingResolved()Z
+Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
+Landroid/view/View;->isVisibleToUser()Z
+Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener;
+Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
+Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo;
+Landroid/view/View;->mBottom:I
+Landroid/view/View;->mContext:Landroid/content/Context;
+Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->mLeft:I
+Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo;
+Landroid/view/View;->mMinHeight:I
+Landroid/view/View;->mMinWidth:I
+Landroid/view/View;->mPaddingLeft:I
+Landroid/view/View;->mPaddingRight:I
+Landroid/view/View;->mParent:Landroid/view/ViewParent;
+Landroid/view/View;->mPrivateFlags3:I
+Landroid/view/View;->mRecreateDisplayList:Z
+Landroid/view/View;->mResources:Landroid/content/res/Resources;
+Landroid/view/View;->mRight:I
+Landroid/view/View;->mScrollCache:Landroid/view/View$ScrollabilityCache;
+Landroid/view/View;->mScrollX:I
+Landroid/view/View;->mScrollY:I
+Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String;
+Landroid/view/View;->mTop:I
+Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
+Landroid/view/View;->recomputePadding()V
+Landroid/view/View;->requestAccessibilityFocus()Z
+Landroid/view/View;->resetPaddingToInitialValues()V
+Landroid/view/ViewRootImpl;->detachFunctor(J)V
+Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
+Landroid/view/ViewRootImpl;->mStopped:Z
+Landroid/view/View;->setAssistBlocked(Z)V
+Landroid/view/View;->setFrame(IIII)Z
+Landroid/view/View;->setIsRootNamespace(Z)V
+Landroid/view/View;->startActivityForResult(Landroid/content/Intent;I)V
+Landroid/view/View;->STATUS_BAR_DISABLE_BACK:I
+Landroid/view/View;->STATUS_BAR_DISABLE_EXPAND:I
+Landroid/view/View;->STATUS_BAR_DISABLE_HOME:I
+Landroid/view/View;->STATUS_BAR_DISABLE_RECENT:I
+Landroid/view/View;->toGlobalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/View;->toLocalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/ViewTreeObserver;->addOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->setTouchableInsets(I)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->TOUCHABLE_INSETS_REGION:I
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->touchableRegion:Landroid/graphics/Region;
+Landroid/view/ViewTreeObserver;->removeOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/WindowManagerGlobal;->getInstance()Landroid/view/WindowManagerGlobal;
+Landroid/view/WindowManagerGlobal;->getRootView(Ljava/lang/String;)Landroid/view/View;
+Landroid/view/WindowManagerGlobal;->getViewRootNames()[Ljava/lang/String;
+Landroid/view/WindowManagerGlobal;->getWindowManagerService()Landroid/view/IWindowManager;
+Landroid/view/WindowManagerGlobal;->mLock:Ljava/lang/Object;
+Landroid/view/WindowManagerGlobal;->mViews:Ljava/util/ArrayList;
+Landroid/view/WindowManagerGlobal;->trimMemory(I)V
+Landroid/view/WindowManager$LayoutParams;->needsMenuKey:I
+Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE:I
+Landroid/view/WindowManager$LayoutParams;->privateFlags:I
+Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J
+Landroid/view/Window;->mAppName:Ljava/lang/String;
+Landroid/view/Window;->mAppToken:Landroid/os/IBinder;
+Landroid/view/Window;->mHardwareAccelerated:Z
+Landroid/webkit/WebSettings;->setNavDump(Z)V
+Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V
+Landroid/webkit/WebView;->debugDump()V
+Landroid/webkit/WebView;->disablePlatformNotifications()V
+Landroid/webkit/WebView;->emulateShiftHeld()V
+Landroid/webkit/WebView;->enablePlatformNotifications()V
+Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo;
+Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebView;->getVisibleTitleHeight()I
+Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->isPaused()Z
+Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->notifyFindDialogDismissed()V
+Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
+Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z
+Landroid/webkit/WebView;->sEnforceThreadChecking:Z
+Landroid/widget/AbsListView$FlingRunnable;->endFling()V
+Landroid/widget/AbsListView;->invokeOnItemScrollListener()V
+Landroid/widget/AbsListView;->isVerticalScrollBarHidden()Z
+Landroid/widget/AbsListView;->mAdapter:Landroid/widget/ListAdapter;
+Landroid/widget/AbsListView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mFastScroll:Landroid/widget/FastScroller;
+Landroid/widget/AbsListView;->mFlingRunnable:Landroid/widget/AbsListView$FlingRunnable;
+Landroid/widget/AbsListView;->mIsChildViewEnabled:Z
+Landroid/widget/AbsListView;->mMaximumVelocity:I
+Landroid/widget/AbsListView;->mMotionPosition:I
+Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
+Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin;
+Landroid/widget/AbsListView;->mSelectionTopPadding:I
+Landroid/widget/AbsListView;->mSelectorPosition:I
+Landroid/widget/AbsListView;->mSelectorRect:Landroid/graphics/Rect;
+Landroid/widget/AbsListView;->mTouchMode:I
+Landroid/widget/AbsListView;->mTouchSlop:I
+Landroid/widget/AbsListView;->mVelocityTracker:Landroid/view/VelocityTracker;
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJFF)Z
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJ)Z
+Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V
+Landroid/widget/AbsListView;->trackMotionScroll(II)Z
+Landroid/widget/AbsSeekBar;->mIsDragging:Z
+Landroid/widget/AbsSeekBar;->mSplitTrack:Z
+Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable;
+Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F
+Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V
+Landroid/widget/AdapterView;->mDataChanged:Z
+Landroid/widget/AdapterView;->setNextSelectedPositionInt(I)V
+Landroid/widget/AdapterView;->setSelectedPositionInt(I)V
+Landroid/widget/AutoCompleteTextView;->doAfterTextChanged()V
+Landroid/widget/AutoCompleteTextView;->doBeforeTextChanged()V
+Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V
+Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
+Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
+Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate;
+Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint;
+Landroid/widget/Editor;->mShowCursor:J
+Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Gallery;->mDownTouchView:Landroid/view/View;
+Landroid/widget/GridView;->mColumnWidth:I
+Landroid/widget/GridView;->mHorizontalSpacing:I
+Landroid/widget/GridView;->mNumColumns:I
+Landroid/widget/GridView;->mRequestedNumColumns:I
+Landroid/widget/GridView;->mVerticalSpacing:I
+Landroid/widget/HorizontalScrollView;->mEdgeGlowLeft:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mEdgeGlowRight:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/ImageView;->mAdjustViewBounds:Z
+Landroid/widget/ImageView;->mAlpha:I
+Landroid/widget/ImageView;->mMaxHeight:I
+Landroid/widget/ImageView;->mMaxWidth:I
+Landroid/widget/LinearLayout;->mGravity:I
+Landroid/widget/LinearLayout;->mUseLargestChild:Z
+Landroid/widget/ListPopupWindow;->mPopup:Landroid/widget/PopupWindow;
+Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V
+Landroid/widget/ListView;->mAreAllItemsSelectable:Z
+Landroid/widget/ListView;->setSelectionInt(I)V
+Landroid/widget/MediaController;->mAnchor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecorLayoutParams:Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/MediaController;->mWindowManager:Landroid/view/WindowManager;
+Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText;
+Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint;
+Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper;
+Landroid/widget/PopupWindow;->computeAnimationResource()I
+Landroid/widget/PopupWindow;->createPopupLayoutParams(Landroid/os/IBinder;)Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/PopupWindow;->invokePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->mAboveAnchor:Z
+Landroid/widget/PopupWindow;->mAnchor:Ljava/lang/ref/WeakReference;
+Landroid/widget/PopupWindow;->mAnimationStyle:I
+Landroid/widget/PopupWindow;->mBackgroundView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mContentView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mHeightMode:I
+Landroid/widget/PopupWindow;->mIsDropdown:Z
+Landroid/widget/PopupWindow;->mIsShowing:Z
+Landroid/widget/PopupWindow;->mLastHeight:I
+Landroid/widget/PopupWindow;->mLastWidth:I
+Landroid/widget/PopupWindow;->mOnScrollChangedListener:Landroid/view/ViewTreeObserver$OnScrollChangedListener;
+Landroid/widget/PopupWindow;->mWidthMode:I
+Landroid/widget/PopupWindow;->preparePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V
+Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V
+Landroid/widget/PopupWindow;->setTouchModal(Z)V
+Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/ProgressBar;->mDuration:I
+Landroid/widget/ProgressBar;->mIndeterminate:Z
+Landroid/widget/ProgressBar;->mMaxHeight:I
+Landroid/widget/ProgressBar;->mMinHeight:I
+Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z
+Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I
+Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I
+Landroid/widget/RelativeLayout$LayoutParams;->mRight:I
+Landroid/widget/RelativeLayout$LayoutParams;->mTop:I
+Landroid/widget/RelativeLayout;->mGravity:I
+Landroid/widget/RemoteViews$Action;->mergeBehavior()I
+Landroid/widget/RemoteViews$Action;->viewId:I
+Landroid/widget/RemoteViews;->mActions:Ljava/util/ArrayList;
+Landroid/widget/RemoteViews;->mApplication:Landroid/content/pm/ApplicationInfo;
+Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mIsBeingDragged:Z
+Landroid/widget/ScrollView;->mOverflingDistance:I
+Landroid/widget/ScrollView;->mOverscrollDistance:I
+Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView;
+Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView;
+Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View;
+Landroid/widget/SearchView;->onCloseClicked()V
+Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V
+Landroid/widget/SlidingDrawer;->mTopOffset:I
+Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
+Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
+Landroid/widget/TextView;->createEditorIfNeeded()V
+Landroid/widget/TextView;->mCursorDrawableRes:I
+Landroid/widget/TextView;->mCurTextColor:I
+Landroid/widget/TextView;->mEditor:Landroid/widget/Editor;
+Landroid/widget/TextView;->mListeners:Ljava/util/ArrayList;
+Landroid/widget/TextView;->mMarquee:Landroid/widget/TextView$Marquee;
+Landroid/widget/TextView;->mMaximum:I
+Landroid/widget/TextView;->mMaxMode:I
+Landroid/widget/TextView;->mSingleLine:Z
+Landroid/widget/VideoView;->mCurrentBufferPercentage:I
+Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController;
+Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback;
+Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
+Landroid/widget/VideoView;->mVideoHeight:I
+Landroid/widget/VideoView;->mVideoWidth:I
+Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
+Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getSensorStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getUid()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWakelockStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getForegroundTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getStarts(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getSystemTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getUserTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getHandle()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getSensorTime()Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Wakelock;->getWakeTime(I)Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D
+Lcom/android/internal/os/PowerProfile;->getBatteryCapacity()D
+Lcom/android/internal/R$array;->config_mobile_hotspot_provision_app:I
+Lcom/android/internal/R$array;->config_tether_wifi_regexs:I
+Lcom/android/internal/R$attr;->switchStyle:I
+Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
+Lcom/android/internal/R$bool;->config_showNavigationBar:I
+Lcom/android/internal/R$dimen;->navigation_bar_height:I
+Lcom/android/internal/R$dimen;->navigation_bar_height_landscape:I
+Lcom/android/internal/R$dimen;->status_bar_height:I
+Lcom/android/internal/R$drawable;->ic_menu_close_clear_cancel:I
+Lcom/android/internal/R$id;->amPm:I
+Lcom/android/internal/R$id;->edittext_container:I
+Lcom/android/internal/R$id;->icon:I
+Lcom/android/internal/R$id;->message:I
+Lcom/android/internal/R$id;->minute:I
+Lcom/android/internal/R$id;->shortcut:I
+Lcom/android/internal/R$id;->text:I
+Lcom/android/internal/R$id;->time:I
+Lcom/android/internal/R$id;->timePicker:I
+Lcom/android/internal/R$id;->title_container:I
+Lcom/android/internal/R$id;->title:I
+Lcom/android/internal/R$integer;->config_screenBrightnessDim:I
+Lcom/android/internal/R$layout;->screen_title:I
+Lcom/android/internal/R$styleable;->CompoundButton_button:I
+Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->IconMenuView:[I
+Lcom/android/internal/R$styleable;->ImageView:[I
+Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ScrollView:[I
+Lcom/android/internal/R$styleable;->TabWidget:[I
+Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
+Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
+Lcom/android/internal/R$styleable;->TextView_drawableRight:I
+Lcom/android/internal/R$styleable;->TextView_drawableTop:I
+Lcom/android/internal/R$styleable;->TextView:[I
+Lcom/android/internal/R$styleable;->View_background:I
+Lcom/android/internal/R$styleable;->View:[I
+Lcom/android/internal/R$styleable;->View_id:I
+Lcom/android/internal/R$styleable;->ViewStub:[I
+Lcom/android/internal/R$styleable;->ViewStub_inflatedId:I
+Lcom/android/internal/R$styleable;->ViewStub_layout:I
+Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$xml;->power_profile:I
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
+Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
+Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephony;->endCall()Z
+Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
+Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
+Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
+Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
+Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I
+Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
+Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
+Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
+Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
+Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
+Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
+Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
+Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
+Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/VMDebug;->dumpReferenceTables()V
+Ldalvik/system/VMRuntime;->clearGrowthLimit()V
+Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
+Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
+Ldalvik/system/VMRuntime;->is64Bit()Z
+Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
+Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
+Ldalvik/system/VMRuntime;->registerNativeFree(I)V
+Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
+Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
+Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
+Ldalvik/system/VMRuntime;->trackExternalFree(J)V
+Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
+Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
+Ljava/io/FileDescriptor;->descriptor:I
+Ljava/io/FileDescriptor;->getInt$()I
+Ljava/io/FileDescriptor;->setInt$(I)V
+Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
+Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
+Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
+Ljava/lang/Class;->dexCache:Ljava/lang/Object;
+Ljava/lang/Class;->dexClassDefIndex:I
+Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
+Ljava/lang/Daemons$Daemon;->stop()V
+Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
+Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
+Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
+Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
+Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup;
+Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
+Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
+Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
+Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
+Ljava/net/InetAddress;->clearDnsCache()V
+Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
+Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
+Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
+Ljava/net/URI;->host:Ljava/lang/String;
+Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
+Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
+Ljava/util/ArrayList$SubList;->parentOffset:I
+Ljava/util/ArrayList$SubList;->size:I
+Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
+Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
+Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
+Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
+Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Lorg/json/JSONArray;->values:Ljava/util/List;
+Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7bfb20f..04c44a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,6 +425,12 @@
  * safely called after {@link #onPause()} and allows and application to safely
  * wait until {@link #onStop()} to save persistent state.</p>
  *
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
  * <p>For those methods that are not marked as being killable, the activity's
  * process will not be killed by the system starting from the time the method
  * is called and continuing after it returns.  Thus an activity is in the killable
@@ -1577,8 +1583,11 @@
      * call through to the default implementation, otherwise be prepared to save
      * all of the state of each view yourself.
      *
-     * <p>If called, this method will occur before {@link #onStop}.  There are
-     * no guarantees about whether it will occur before or after {@link #onPause}.
+     * <p>If called, this method will occur after {@link #onStop} for applications
+     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+     * For applications targeting earlier platform versions this method will occur
+     * before {@link #onStop} and there are no guarantees about whether it will
+     * occur before or after {@link #onPause}.
      *
      * @param outState Bundle in which to place your saved state.
      *
@@ -7143,7 +7152,10 @@
         boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
 
         if (isAppDebuggable || isApiWarningEnabled) {
-            if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+            if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
+                // Only show the warning once per process.
+                mMainThread.mHiddenApiWarningShown = true;
+
                 String appName = getApplicationInfo().loadLabel(getPackageManager())
                         .toString();
                 String warning = "Detected problems with API compatiblity\n"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6c3dbf4..42825f0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -291,6 +291,7 @@
     boolean mJitEnabled = false;
     boolean mSomeActivitiesChanged = false;
     boolean mUpdatingSystemConfig = false;
+    /* package */ boolean mHiddenApiWarningShown = false;
 
     // These can be accessed by multiple threads; mResourcesManager is the lock.
     // XXX For now we keep around information about all packages we have
@@ -487,12 +488,14 @@
             }
         }
 
-        public boolean isPreHoneycomb() {
-            if (activity != null) {
-                return activity.getApplicationInfo().targetSdkVersion
-                        < android.os.Build.VERSION_CODES.HONEYCOMB;
-            }
-            return false;
+        private boolean isPreHoneycomb() {
+            return activity != null && activity.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.HONEYCOMB;
+        }
+
+        private boolean isPreP() {
+            return activity != null && activity.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.P;
         }
 
         public boolean isPersistable() {
@@ -3969,8 +3972,7 @@
             }
 
             r.activity.mConfigChangeFlags |= configChanges;
-            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity",
-                    pendingActions);
+            performPauseActivity(r, finished, "handlePauseActivity", pendingActions);
 
             // Make sure any pending writes are now committed.
             if (r.isPreHoneycomb()) {
@@ -3993,16 +3995,18 @@
         mInstrumentation.callActivityOnUserLeaving(r.activity);
     }
 
-    final Bundle performPauseActivity(IBinder token, boolean finished,
-            boolean saveState, String reason, PendingTransactionActions pendingActions) {
+    final Bundle performPauseActivity(IBinder token, boolean finished, String reason,
+            PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        return r != null
-                ? performPauseActivity(r, finished, saveState, reason, pendingActions)
-                : null;
+        return r != null ? performPauseActivity(r, finished, reason, pendingActions) : null;
     }
 
-    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, boolean saveState,
-            String reason, PendingTransactionActions pendingActions) {
+    /**
+     * Pause the activity.
+     * @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise.
+     */
+    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
+            PendingTransactionActions pendingActions) {
         if (r.paused) {
             if (r.activity.mFinished) {
                 // If we are finishing, we won't call onResume() in certain cases.
@@ -4019,9 +4023,10 @@
             r.activity.mFinished = true;
         }
 
-        // Next have the activity save its current state and managed dialogs...
-        if (!r.activity.mFinished && saveState) {
-            callCallActivityOnSaveInstanceState(r);
+        // Pre-Honeycomb apps always save their state before pausing
+        final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
+        if (shouldSaveState) {
+            callActivityOnSaveInstanceState(r);
         }
 
         performPauseActivityIfNeeded(r, reason);
@@ -4048,7 +4053,7 @@
             }
         }
 
-        return !r.activity.mFinished && saveState ? r.state : null;
+        return shouldSaveState ? r.state : null;
     }
 
     private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
@@ -4149,30 +4154,47 @@
                 }
             }
 
-            // Next have the activity save its current state and managed dialogs...
-            if (!r.activity.mFinished && saveState) {
-                if (r.state == null) {
-                    callCallActivityOnSaveInstanceState(r);
-                }
-            }
-
             if (!keepShown) {
-                try {
-                    // Now we are idle.
-                    r.activity.performStop(false /*preserveWindow*/);
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
+                callActivityOnStop(r, saveState, reason);
+            }
+        }
+    }
+
+    /**
+     * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
+     * the client record's state.
+     * All calls to stop an activity must be done through this method to make sure that
+     * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
+     */
+    private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+        // Before P onSaveInstanceState was called before onStop, starting with P it's
+        // called after. Before Honeycomb state was always saved before onPause.
+        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
+                && !r.isPreHoneycomb();
+        final boolean isPreP = r.isPreP();
+        if (shouldSaveState && isPreP) {
+            callActivityOnSaveInstanceState(r);
+        }
+
+        try {
+            r.activity.performStop(false /*preserveWindow*/);
+        } catch (SuperNotCalledException e) {
+            throw e;
+        } catch (Exception e) {
+            if (!mInstrumentation.onException(r.activity, e)) {
+                throw new RuntimeException(
+                        "Unable to stop activity "
                                 + r.intent.getComponent().toShortString()
                                 + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), reason);
             }
         }
+        r.setState(ON_STOP);
+        EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
+                r.activity.getComponentName().getClassName(), reason);
+
+        if (shouldSaveState && !isPreP) {
+            callActivityOnSaveInstanceState(r);
+        }
     }
 
     private void updateVisibility(ActivityClientRecord r, boolean show) {
@@ -4292,24 +4314,7 @@
 
         if (sleeping) {
             if (!r.stopped && !r.isPreHoneycomb()) {
-                if (!r.activity.mFinished && r.state == null) {
-                    callCallActivityOnSaveInstanceState(r);
-                }
-
-                try {
-                    // Now we are idle.
-                    r.activity.performStop(false /*preserveWindow*/);
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
-                                + r.intent.getComponent().toShortString()
-                                + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), "sleeping");
+                callActivityOnStop(r, true /* saveState */, "sleeping");
             }
 
             // Make sure any pending writes are now committed.
@@ -4459,21 +4464,7 @@
             performPauseActivityIfNeeded(r, "destroy");
 
             if (!r.stopped) {
-                try {
-                    r.activity.performStop(r.mPreserveWindow);
-                } catch (SuperNotCalledException e) {
-                    throw e;
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
-                                + safeToComponentShortString(r.intent)
-                                + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), "destroy");
+                callActivityOnStop(r, false /* saveState */, "destroy");
             }
             if (getNonConfigInstance) {
                 try {
@@ -4779,11 +4770,11 @@
 
         // Need to ensure state is saved.
         if (!r.paused) {
-            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity",
+            performPauseActivity(r, false, "handleRelaunchActivity",
                     null /* pendingActions */);
         }
-        if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
-            callCallActivityOnSaveInstanceState(r);
+        if (!r.stopped) {
+            callActivityOnStop(r, true /* saveState */, "handleRelaunchActivity");
         }
 
         handleDestroyActivity(r.token, false, configChanges, true);
@@ -4816,8 +4807,7 @@
         handleStartActivity(r, pendingActions);
         handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
         if (r.startsNotResumed) {
-            performPauseActivity(r, false /* finished */, r.isPreHoneycomb(), "relaunch",
-                    pendingActions);
+            performPauseActivity(r, false /* finished */, "relaunch", pendingActions);
         }
 
         if (!tmp.onlyLocalRequest) {
@@ -4832,7 +4822,7 @@
         }
     }
 
-    private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
+    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
         r.state = new Bundle();
         r.state.setAllowFds(false);
         if (r.isPersistable()) {
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
deleted file mode 100644
index d1c2441..0000000
--- a/core/java/android/app/EphemeralResolverService.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.annotation.SystemApi;
-import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.InstantAppResolveInfo;
-import android.os.Build;
-import android.os.Looper;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Base class for implementing the resolver service.
- * @hide
- * @removed
- * @deprecated use InstantAppResolverService instead
- */
-@Deprecated
-@SystemApi
-public abstract class EphemeralResolverService extends InstantAppResolverService {
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
-    private static final String TAG = "PackageManager";
-
-    /**
-     * Called to retrieve resolve info for ephemeral applications.
-     *
-     * @param digestPrefix The hash prefix of the ephemeral's domain.
-     * @param prefixMask A mask that was applied to each digest prefix. This should
-     *      be used when comparing against the digest prefixes as all bits might
-     *      not be set.
-     * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead
-     */
-    @Deprecated
-    public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
-            int digestPrefix[], int prefix);
-
-    /**
-     * Called to retrieve resolve info for ephemeral applications.
-     *
-     * @param digestPrefix The hash prefix of the ephemeral's domain.
-     */
-    public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) {
-        return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000);
-    }
-
-    /**
-     * Called to retrieve intent filters for ephemeral applications.
-     *
-     * @param hostName The name of the host to get intent filters for.
-     */
-    public EphemeralResolveInfo onGetEphemeralIntentFilter(String hostName) {
-        throw new IllegalStateException("Must define");
-    }
-
-    @Override
-    public Looper getLooper() {
-        return super.getLooper();
-    }
-
-    void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
-            InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Log.d(TAG, "Legacy resolver; getInstantAppResolveInfo;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        final List<EphemeralResolveInfo> response = onGetEphemeralResolveInfo(digestPrefix);
-        final int responseSize = response == null ? 0 : response.size();
-        final List<InstantAppResolveInfo> resultList = new ArrayList<>(responseSize);
-        for (int i = 0; i < responseSize; i++) {
-            resultList.add(response.get(i).getInstantAppResolveInfo());
-        }
-        callback.onInstantAppResolveInfo(resultList);
-    }
-
-    void _onGetInstantAppIntentFilter(int[] digestPrefix, String token,
-            String hostName, InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Log.d(TAG, "Legacy resolver; getInstantAppIntentFilter;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName);
-        final List<InstantAppResolveInfo> resultList = new ArrayList<>(1);
-        resultList.add(response.getInstantAppResolveInfo());
-        callback.onInstantAppResolveInfo(resultList);
-    }
-}
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 89aff36..76a3682 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -43,7 +43,7 @@
  */
 @SystemApi
 public abstract class InstantAppResolverService extends Service {
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
     private static final String TAG = "PackageManager";
 
     /** @hide */
@@ -133,7 +133,7 @@
             @Override
             public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix,
                     String token, int sequence, IRemoteCallback callback) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.v(TAG, "[" + token + "] Phase1 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
@@ -148,7 +148,7 @@
             @Override
             public void getInstantAppIntentFilterList(Intent sanitizedIntent,
                     int[] digestPrefix, String token, IRemoteCallback callback) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.v(TAG, "[" + token + "] Phase2 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
@@ -203,7 +203,7 @@
                     final String token = (String) args.arg3;
                     final Intent intent = (Intent) args.arg4;
                     final int sequence = message.arg1;
-                    if (DEBUG_EPHEMERAL) {
+                    if (DEBUG_INSTANT) {
                         Slog.d(TAG, "[" + token + "] Phase1 request;"
                                 + " prefix: " + Arrays.toString(digestPrefix));
                     }
@@ -217,7 +217,7 @@
                     final int[] digestPrefix = (int[]) args.arg2;
                     final String token = (String) args.arg3;
                     final Intent intent = (Intent) args.arg4;
-                    if (DEBUG_EPHEMERAL) {
+                    if (DEBUG_INSTANT) {
                         Slog.d(TAG, "[" + token + "] Phase2 request;"
                                 + " prefix: " + Arrays.toString(digestPrefix));
                     }
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index c34f4d9..1d34595 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -224,8 +224,8 @@
     
     private void performPause(LocalActivityRecord r, boolean finishing) {
         final boolean needState = r.instanceState == null;
-        final Bundle instanceState = mActivityThread.performPauseActivity(
-                r, finishing, needState, "performPause", null /* pendingActions */);
+        final Bundle instanceState = mActivityThread.performPauseActivity(r, finishing,
+                "performPause", null /* pendingActions */);
         if (needState) {
             r.instanceState = instanceState;
         }
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 085fc79..46566e7 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -146,6 +146,9 @@
     })
     public @interface WindowConfig {}
 
+    /** @hide */
+    public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5;
+
     public WindowConfiguration() {
         unset();
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 131abb5..e190fd4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6609,15 +6609,81 @@
     }
 
     /**
+     * Indicates user operation is successful.
+     *
+     * @see #startUserInBackground(ComponentName, UserHandle)
+     * @see #stopUser(ComponentName, UserHandle)
+     * @see #logoutUser(ComponentName)
+     */
+    public static final int USER_OPERATION_SUCCESS = 0;
+
+    /**
+     * Indicates user operation failed for unknown reason.
+     *
+     * @see #startUserInBackground(ComponentName, UserHandle)
+     * @see #stopUser(ComponentName, UserHandle)
+     * @see #logoutUser(ComponentName)
+     */
+    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
+
+    /**
+     * Indicates user operation failed because target user is a managed profile.
+     *
+     * @see #startUserInBackground(ComponentName, UserHandle)
+     * @see #stopUser(ComponentName, UserHandle)
+     * @see #logoutUser(ComponentName)
+     */
+    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
+
+    /**
+     * Indicates user operation failed because maximum running user limit has reached.
+     *
+     * @see #startUserInBackground(ComponentName, UserHandle)
+     */
+    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
+
+    /**
+     * Indicates user operation failed because the target user is in foreground.
+     *
+     * @see #stopUser(ComponentName, UserHandle)
+     * @see #logoutUser(ComponentName)
+     */
+    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
+
+    /**
+     * Result returned from
+     * <ul>
+     * <li>{@link #startUserInBackground(ComponentName, UserHandle)}</li>
+     * <li>{@link #stopUser(ComponentName, UserHandle)}</li>
+     * <li>{@link #logoutUser(ComponentName)}</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "USER_OPERATION_" }, value = {
+            USER_OPERATION_SUCCESS,
+            USER_OPERATION_ERROR_UNKNOWN,
+            USER_OPERATION_ERROR_MANAGED_PROFILE,
+            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
+            USER_OPERATION_ERROR_CURRENT_USER
+    })
+    public @interface UserOperationResult {}
+
+    /**
      * Called by a device owner to start the specified secondary user in background.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param userHandle the user to be stopped.
-     * @return {@code true} if the user can be started, {@code false} otherwise.
+     * @param userHandle the user to be started in background.
+     * @return one of the following result codes:
+     * {@link #USER_OPERATION_ERROR_UNKNOWN},
+     * {@link #USER_OPERATION_SUCCESS},
+     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link #USER_OPERATION_ERROR_MAX_RUNNING_USERS},
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean startUserInBackground(
+    public @UserOperationResult int startUserInBackground(
             @NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("startUserInBackground");
         try {
@@ -6632,11 +6698,16 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to be stopped.
-     * @return {@code true} if the user can be stopped, {@code false} otherwise.
+     * @return one of the following result codes:
+     * {@link #USER_OPERATION_ERROR_UNKNOWN},
+     * {@link #USER_OPERATION_SUCCESS},
+     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
+    public @UserOperationResult int stopUser(
+            @NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("stopUser");
         try {
             return mService.stopUser(admin, userHandle);
@@ -6650,11 +6721,15 @@
      * calling user and switch back to primary.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return {@code true} if the exit was successful, {@code false} otherwise.
+     * @return one of the following result codes:
+     * {@link #USER_OPERATION_ERROR_UNKNOWN},
+     * {@link #USER_OPERATION_SUCCESS},
+     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean logoutUser(@NonNull ComponentName admin) {
+    public @UserOperationResult int logoutUser(@NonNull ComponentName admin) {
         throwIfParentInstance("logoutUser");
         try {
             return mService.logoutUser(admin);
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cba9311..5197de4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -227,9 +227,9 @@
     UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
-    boolean startUserInBackground(in ComponentName who, in UserHandle userHandle);
-    boolean stopUser(in ComponentName who, in UserHandle userHandle);
-    boolean logoutUser(in ComponentName who);
+    int startUserInBackground(in ComponentName who, in UserHandle userHandle);
+    int stopUser(in ComponentName who, in UserHandle userHandle);
+    int logoutUser(in ComponentName who);
     List<UserHandle> getSecondaryUsers(in ComponentName who);
 
     void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 4c24a2d..126deef 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -164,9 +164,15 @@
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     /**
      * Key to retrieve an extra added to an intent when the value of a slider is changed.
+     * @deprecated remove once support lib is update to use EXTRA_RANGE_VALUE instead
      */
+    @Deprecated
     public static final String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
     /**
+     * Key to retrieve an extra added to an intent when the value of an input range is changed.
+     */
+    public static final String EXTRA_RANGE_VALUE = "android.app.slice.extra.RANGE_VALUE";
+    /**
      * Subtype to indicate that this is a message as part of a communication
      * sequence in this slice.
      */
@@ -181,14 +187,20 @@
     public static final String SUBTYPE_COLOR = "color";
     /**
      * Subtype to tag an item as representing a slider.
+     * @deprecated remove once support lib is update to use SUBTYPE_RANGE instead
      */
+    @Deprecated
     public static final String SUBTYPE_SLIDER = "slider";
     /**
-     * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_SLIDER}.
+     * Subtype to tag an item as representing a range.
+     */
+    public static final String SUBTYPE_RANGE = "range";
+    /**
+     * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_RANGE}.
      */
     public static final String SUBTYPE_MAX = "max";
     /**
-     * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_SLIDER}.
+     * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_RANGE}.
      */
     public static final String SUBTYPE_VALUE = "value";
     /**
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 2fa9d8e..e5f3eae 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -314,6 +314,59 @@
     }
 
     /**
+     * Turns a slice intent into a slice uri. Expects an explicit intent. If there is no
+     * {@link android.content.ContentProvider} associated with the given intent this will throw
+     * {@link IllegalArgumentException}.
+     *
+     * @param intent The intent associated with a slice.
+     * @return The Slice Uri provided by the app or null if none is given.
+     * @see Slice
+     * @see SliceProvider#onMapIntentToUri(Intent)
+     * @see Intent
+     */
+    public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
+        Preconditions.checkNotNull(intent, "intent");
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+                "Slice intent must be explicit %s", intent);
+        ContentResolver resolver = mContext.getContentResolver();
+
+        // Check if the intent has data for the slice uri on it and use that
+        final Uri intentData = intent.getData();
+        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+            return intentData;
+        }
+        // Otherwise ask the app
+        List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+        if (providers == null || providers.isEmpty()) {
+            throw new IllegalArgumentException("Unable to resolve intent " + intent);
+        }
+        String authority = providers.get(0).providerInfo.authority;
+        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).build();
+        IContentProvider provider = resolver.acquireProvider(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+        try {
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            final Bundle res = provider.call(mContext.getPackageName(),
+                    SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+            if (res == null) {
+                return null;
+            }
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            resolver.releaseProvider(provider);
+        }
+    }
+
+    /**
      * Turns a slice intent into slice content. Expects an explicit intent. If there is no
      * {@link android.content.ContentProvider} associated with the given intent this will throw
      * {@link IllegalArgumentException}.
@@ -329,7 +382,7 @@
             @NonNull List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
-                "Slice intent must be explicit " + intent);
+                "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
 
         // Check if the intent has data for the slice uri on it and use that
@@ -340,7 +393,7 @@
         // Otherwise ask the app
         List<ResolveInfo> providers =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
-        if (providers == null) {
+        if (providers == null || providers.isEmpty()) {
             throw new IllegalArgumentException("Unable to resolve intent " + intent);
         }
         String authority = providers.get(0).providerInfo.authority;
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 00e8cca..af43032 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -114,6 +114,10 @@
     /**
      * @hide
      */
+    public static final String METHOD_MAP_ONLY_INTENT = "map_only";
+    /**
+     * @hide
+     */
     public static final String METHOD_PIN = "pin";
     /**
      * @hide
@@ -147,6 +151,14 @@
      * @hide
      */
     public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_OVERRIDE_UID = "override_uid";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_OVERRIDE_PID = "override_pid";
 
     private static final boolean DEBUG = false;
 
@@ -302,13 +314,20 @@
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
             String callingPackage = getCallingPackage();
+            int callingUid = Binder.getCallingUid();
+            int callingPid = Binder.getCallingPid();
             if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
                 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                     throw new SecurityException("Only the system can override calling pkg");
                 }
+                // This is safe because we would grant SYSTEM_UID access to all slices
+                // and want to allow it to bind slices as if it were a less privileged app
+                // to check their permission levels.
                 callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
+                callingUid = extras.getInt(EXTRA_OVERRIDE_UID);
+                callingPid = extras.getInt(EXTRA_OVERRIDE_PID);
             }
-            Slice s = handleBindSlice(uri, supportedSpecs, callingPackage);
+            Slice s = handleBindSlice(uri, supportedSpecs, callingPackage, callingUid, callingPid);
             Bundle b = new Bundle();
             b.putParcelable(EXTRA_SLICE, s);
             return b;
@@ -319,12 +338,20 @@
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
             Bundle b = new Bundle();
             if (uri != null) {
-                Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage());
+                Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage(),
+                        Binder.getCallingUid(), Binder.getCallingPid());
                 b.putParcelable(EXTRA_SLICE, s);
             } else {
                 b.putParcelable(EXTRA_SLICE, null);
             }
             return b;
+        } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
+            Intent intent = extras.getParcelable(EXTRA_INTENT);
+            if (intent == null) return null;
+            Uri uri = onMapIntentToUri(intent);
+            Bundle b = new Bundle();
+            b.putParcelable(EXTRA_SLICE, uri);
+            return b;
         } else if (method.equals(METHOD_PIN)) {
             Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -401,15 +428,15 @@
     }
 
     private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
-            String callingPkg) {
+            String callingPkg, int callingUid, int callingPid) {
         // This can be removed once Slice#bindSlice is removed and everyone is using
         // SliceManager#bindSlice.
         String pkg = callingPkg != null ? callingPkg
-                : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
-        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+                : getContext().getPackageManager().getNameForUid(callingUid);
+        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
             try {
                 mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                        Binder.getCallingPid(), Binder.getCallingUid());
+                        callingPid, callingUid);
             } catch (SecurityException e) {
                 return createPermissionSlice(getContext(), sliceUri, pkg);
             }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b3c8737..9b62f19 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1554,16 +1554,6 @@
     public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE
-            = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
-    /**
      * Activity Action: Launch instant application installer.
      * <p class="note">
      * This is a protected intent that can only be sent by the system.
@@ -1577,16 +1567,6 @@
             = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE
-            = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
-    /**
      * Service Action: Resolve instant application.
      * <p>
      * The system will have a persistent connection to this service.
@@ -1601,16 +1581,6 @@
             = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS
-            = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
-    /**
      * Activity Action: Launch instant app settings.
      *
      * <p class="note">
@@ -4444,32 +4414,77 @@
 
     /**
      * A {@link IntentSender} to start after ephemeral installation success.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS).
+     * @removed
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
 
     /**
-     * A {@link IntentSender} to start after ephemeral installation failure.
+     * A {@link IntentSender} to start after instant app installation success.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_SUCCESS =
+            "android.intent.extra.INSTANT_APP_SUCCESS";
+
+    /**
+     * A {@link IntentSender} to start after ephemeral installation failure.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
 
     /**
-     * The host name that triggered an ephemeral resolution.
+     * A {@link IntentSender} to start after instant app installation failure.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_FAILURE =
+            "android.intent.extra.INSTANT_APP_FAILURE";
+
+    /**
+     * The host name that triggered an ephemeral resolution.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
 
     /**
-     * An opaque token to track ephemeral resolution.
+     * The host name that triggered an instant app resolution.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_HOSTNAME =
+            "android.intent.extra.INSTANT_APP_HOSTNAME";
+
+    /**
+     * An opaque token to track ephemeral resolution.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
 
     /**
+     * An opaque token to track instant app resolution.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_TOKEN =
+            "android.intent.extra.INSTANT_APP_TOKEN";
+
+    /**
      * The action that triggered an instant application resolution.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
 
     /**
@@ -4487,6 +4502,7 @@
      * instant application resolver.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_INSTANT_APP_EXTRAS =
             "android.intent.extra.INSTANT_APP_EXTRAS";
 
@@ -4512,12 +4528,14 @@
      * The version code of the app to install components from.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
 
     /**
-     * The app that triggered the ephemeral installation.
+     * The app that triggered the instant app installation.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_CALLING_PACKAGE
             = "android.intent.extra.CALLING_PACKAGE";
 
@@ -4526,6 +4544,7 @@
      * installer may use.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_VERIFICATION_BUNDLE
             = "android.intent.extra.VERIFICATION_BUNDLE";
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f6697e8..b61a6d9 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -958,6 +958,7 @@
      * Version of the sandbox the application wants to run in.
      * @hide
      */
+    @SystemApi
     public int targetSandboxVersion;
 
     /**
@@ -1600,7 +1601,7 @@
      * @hide
      */
     public boolean isAllowedToUseHiddenApi() {
-        return isSystemApp();
+        return false;
     }
 
     /**
@@ -1655,7 +1656,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
 
-    /** @hide */
+    /**
+     * True if the application is installed as an instant app.
+     * @hide
+     */
+    @SystemApi
     public boolean isInstantApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
     }
diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java
deleted file mode 100644
index 1dbbf81..0000000
--- a/core/java/android/content/pm/EphemeralIntentFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about an ephemeral application intent filter.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralIntentFilter implements Parcelable {
-    private final InstantAppIntentFilter mInstantAppIntentFilter;
-
-    public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
-        mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters);
-    }
-
-    EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) {
-        mInstantAppIntentFilter = intentFilter;
-    }
-
-    EphemeralIntentFilter(Parcel in) {
-        mInstantAppIntentFilter = in.readParcelable(null /*loader*/);
-    }
-
-    public String getSplitName() {
-        return mInstantAppIntentFilter.getSplitName();
-    }
-
-    public List<IntentFilter> getFilters() {
-        return mInstantAppIntentFilter.getFilters();
-    }
-
-    /** @hide */
-    InstantAppIntentFilter getInstantAppIntentFilter() {
-        return mInstantAppIntentFilter;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mInstantAppIntentFilter, flags);
-    }
-
-    public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
-            = new Parcelable.Creator<EphemeralIntentFilter>() {
-        @Override
-        public EphemeralIntentFilter createFromParcel(Parcel in) {
-            return new EphemeralIntentFilter(in);
-        }
-        @Override
-        public EphemeralIntentFilter[] newArray(int size) {
-            return new EphemeralIntentFilter[size];
-        }
-    };
-}
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
deleted file mode 100644
index 12131a3..0000000
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Information about an ephemeral application.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralResolveInfo implements Parcelable {
-    /** Algorithm that will be used to generate the domain digest */
-    public static final String SHA_ALGORITHM = "SHA-256";
-
-    private final InstantAppResolveInfo mInstantAppResolveInfo;
-    @Deprecated
-    private final List<IntentFilter> mLegacyFilters;
-
-    @Deprecated
-    public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
-            @NonNull List<IntentFilter> filters) {
-        if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
-            throw new IllegalArgumentException();
-        }
-        final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1);
-        ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters));
-        mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName,
-                createInstantAppIntentFilterList(ephemeralFilters));
-        mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
-        mLegacyFilters.addAll(filters);
-    }
-
-    @Deprecated
-    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters) {
-        this(digest, packageName, filters, -1 /*versionCode*/);
-    }
-
-    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters, int versionCode) {
-        mInstantAppResolveInfo = new InstantAppResolveInfo(
-                digest.getInstantAppDigest(), packageName,
-                createInstantAppIntentFilterList(filters), versionCode);
-        mLegacyFilters = null;
-    }
-
-    public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters) {
-        this(new EphemeralDigest(hostName), packageName, filters);
-    }
-
-    EphemeralResolveInfo(Parcel in) {
-        mInstantAppResolveInfo = in.readParcelable(null /*loader*/);
-        mLegacyFilters = new ArrayList<IntentFilter>();
-        in.readList(mLegacyFilters, null /*loader*/);
-    }
-
-    /** @hide */
-    public InstantAppResolveInfo getInstantAppResolveInfo() {
-        return mInstantAppResolveInfo;
-    }
-
-    private static List<InstantAppIntentFilter> createInstantAppIntentFilterList(
-            List<EphemeralIntentFilter> filters) {
-        if (filters == null) {
-            return null;
-        }
-        final int filterCount = filters.size();
-        final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount);
-        for (int i = 0; i < filterCount; i++) {
-            returnList.add(filters.get(i).getInstantAppIntentFilter());
-        }
-        return returnList;
-    }
-
-    public byte[] getDigestBytes() {
-        return mInstantAppResolveInfo.getDigestBytes();
-    }
-
-    public int getDigestPrefix() {
-        return mInstantAppResolveInfo.getDigestPrefix();
-    }
-
-    public String getPackageName() {
-        return mInstantAppResolveInfo.getPackageName();
-    }
-
-    public List<EphemeralIntentFilter> getIntentFilters() {
-        final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters();
-        final int filterCount = filters.size();
-        final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount);
-        for (int i = 0; i < filterCount; i++) {
-            returnList.add(new EphemeralIntentFilter(filters.get(i)));
-        }
-        return returnList;
-    }
-
-    public int getVersionCode() {
-        return mInstantAppResolveInfo.getVersionCode();
-    }
-
-    @Deprecated
-    public List<IntentFilter> getFilters() {
-        return mLegacyFilters;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mInstantAppResolveInfo, flags);
-        out.writeList(mLegacyFilters);
-    }
-
-    public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
-            = new Parcelable.Creator<EphemeralResolveInfo>() {
-        @Override
-        public EphemeralResolveInfo createFromParcel(Parcel in) {
-            return new EphemeralResolveInfo(in);
-        }
-        @Override
-        public EphemeralResolveInfo[] newArray(int size) {
-            return new EphemeralResolveInfo[size];
-        }
-    };
-
-    /**
-     * Helper class to generate and store each of the digests and prefixes
-     * sent to the Ephemeral Resolver.
-     * <p>
-     * Since intent filters may want to handle multiple hosts within a
-     * domain [eg “*.google.com”], the resolver is presented with multiple
-     * hash prefixes. For example, "a.b.c.d.e" generates digests for
-     * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class EphemeralDigest implements Parcelable {
-        private final InstantAppDigest mInstantAppDigest;
-
-        public EphemeralDigest(@NonNull String hostName) {
-            this(hostName, -1 /*maxDigests*/);
-        }
-
-        /** @hide */
-        public EphemeralDigest(@NonNull String hostName, int maxDigests) {
-            mInstantAppDigest = new InstantAppDigest(hostName, maxDigests);
-        }
-
-        EphemeralDigest(Parcel in) {
-            mInstantAppDigest = in.readParcelable(null /*loader*/);
-        }
-
-        /** @hide */
-        InstantAppDigest getInstantAppDigest() {
-            return mInstantAppDigest;
-        }
-
-        public byte[][] getDigestBytes() {
-            return mInstantAppDigest.getDigestBytes();
-        }
-
-        public int[] getDigestPrefix() {
-            return mInstantAppDigest.getDigestPrefix();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeParcelable(mInstantAppDigest, flags);
-        }
-
-        @SuppressWarnings("hiding")
-        public static final Parcelable.Creator<EphemeralDigest> CREATOR =
-                new Parcelable.Creator<EphemeralDigest>() {
-            @Override
-            public EphemeralDigest createFromParcel(Parcel in) {
-                return new EphemeralDigest(in);
-            }
-            @Override
-            public EphemeralDigest[] newArray(int size) {
-                return new EphemeralDigest[size];
-            }
-        };
-    }
-}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3d26af1..2da8937 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -78,8 +78,10 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Base64;
+import android.util.ByteStringUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -5683,7 +5685,10 @@
         return true;
     }
 
-    /** A container for signing-related data of an application package. */
+    /**
+     *  A container for signing-related data of an application package.
+     * @hide
+     */
     public static final class SigningDetails implements Parcelable {
 
         @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
@@ -5705,15 +5710,54 @@
         public final ArraySet<PublicKey> publicKeys;
 
         /**
-         * Collection of {@code Signature} objects, each of which is formed from a former signing
-         * certificate of this APK before it was changed by signing certificate rotation.
+         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+         * contains two pieces of information:
+         *   1) the past signing certificates
+         *   2) the flags that APK wants to assign to each of the past signing certificates.
+         *
+         * This collection of {@code Signature} objects, each of which is formed from a former
+         * signing certificate of this APK before it was changed by signing certificate rotation,
+         * represents the first piece of information.  It is the APK saying to the rest of the
+         * world: "hey if you trust the old cert, you can trust me!"  This is useful, if for
+         * instance, the platform would like to determine whether or not to allow this APK to do
+         * something it would've allowed it to do under the old cert (like upgrade).
          */
         @Nullable
         public final Signature[] pastSigningCertificates;
 
+        /** special value used to see if cert is in package - not exposed to callers */
+        private static final int PAST_CERT_EXISTS = 0;
+
+        @IntDef(
+                flag = true,
+                value = {CertCapabilities.INSTALLED_DATA,
+                        CertCapabilities.SHARED_USER_ID,
+                        CertCapabilities.PERMISSION })
+        public @interface CertCapabilities {
+
+            /** accept data from already installed pkg with this cert */
+            int INSTALLED_DATA = 1;
+
+            /** accept sharedUserId with pkg with this cert */
+            int SHARED_USER_ID = 2;
+
+            /** grant SIGNATURE permissions to pkgs with this cert */
+            int PERMISSION = 4;
+        }
+
         /**
-         * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
-         * the including APK wishes to grant to its past signing certificates.
+         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+         * contains two pieces of information:
+         *   1) the past signing certificates
+         *   2) the flags that APK wants to assign to each of the past signing certificates.
+         *
+         * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
+         * collection, represent the second piece of information and are viewed as capabilities.
+         * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+         * please enforce that." This is useful for situation where this app itself is using its
+         * signing certificate as an authorization mechanism, like whether or not to allow another
+         * app to have its SIGNATURE permission.  An app could specify whether to allow other apps
+         * signed by its old cert 'X' to still get a signature permission it defines, for example.
          */
         @Nullable
         public final int[] pastSigningCertificatesFlags;
@@ -5784,6 +5828,244 @@
             return pastSigningCertificates != null && pastSigningCertificates.length > 0;
         }
 
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+         * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+         * then that means it has authorized a signing certificate rotation, which eventually leads
+         * to our certificate, and thus can be trusted. If this method evaluates to true, this
+         * SigningDetails object should be trusted if the previous one is.
+         */
+        public boolean hasAncestorOrSelf(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we just compare current
+                // signers for an exact match
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates
+                return hasCertificate(oldDetails.signatures[0]);
+            }
+        }
+
+        /**
+         * Similar to {@code hasAncestorOrSelf}.  Returns true only if this {@code SigningDetails}
+         * is a descendant of {@code oldDetails}, not if they're the same.  This is used to
+         * determine if this object is newer than the provided one.
+         */
+        public boolean hasAncestor(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (this.hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // the last entry in pastSigningCertificates is the current signer, ignore it
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(oldDetails.signatures[i])) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+         * not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         */
+        public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we must have an exact
+                // match, which also means all capabilities are granted
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates, and if we grant it the capability it's
+                // requesting
+                return hasCertificate(oldDetails.signatures[0], flags);
+            }
+        }
+
+        /**
+         * A special case of {@code checkCapability} which re-encodes both sets of signing
+         * certificates to counteract a previous re-encoding.
+         */
+        public boolean checkCapabilityRecover(SigningDetails oldDetails,
+                @CertCapabilities int flags) throws CertificateException {
+            if (oldDetails == UNKNOWN || this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // signing certificates may have rotated, check entire history for effective match
+                for (int i = 0; i < pastSigningCertificates.length; i++) {
+                    if (Signature.areEffectiveMatch(
+                            oldDetails.signatures[0],
+                            pastSigningCertificates[i])
+                            && pastSigningCertificatesFlags[i] == flags) {
+                        return true;
+                    }
+                }
+            } else {
+                return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+            }
+            return false;
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer.  Automatically returns false if this object has multiple
+         * signing certificates, since rotation is only supported for single-signers; this is
+         * enforced by {@code hasCertificateInternal}.
+         */
+        public boolean hasCertificate(Signature signature) {
+            return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer, and whether or not it has the given permission.
+         * Certificates which match our current signer automatically get all capabilities.
+         * Automatically returns false if this object has multiple signing certificates, since
+         * rotation is only supported for single-signers.
+         */
+        public boolean hasCertificate(Signature signature, @CertCapabilities int flags) {
+            return hasCertificateInternal(signature, flags);
+        }
+
+        /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+        public boolean hasCertificate(byte[] certificate) {
+            Signature signature = new Signature(certificate);
+            return hasCertificate(signature);
+        }
+
+        private boolean hasCertificateInternal(Signature signature, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // only single-signed apps can have pastSigningCertificates
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the current one, which automatically gets all
+                // capabilities, since it is the same as the current signature
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(signature)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer and make sure
+            // we are singly-signed
+            return signatures.length == 1 && signatures[0].equals(signature);
+        }
+
+        /**
+         * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+         * or not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         *
+         * @param sha256String A hex-encoded representation of a sha256 digest.  In the case of an
+         *                     app with multiple signers, this represents the hex-encoded sha256
+         *                     digest of the combined hex-encoded sha256 digests of each individual
+         *                     signing certificate according to {@link
+         *                     PackageUtils#computeSignaturesSha256Digest(Signature[])}
+         */
+        public boolean checkCapability(String sha256String, @CertCapabilities int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // first see if the hash represents a single-signer in our signing history
+            byte[] sha256Bytes = ByteStringUtils.fromHexToByteArray(sha256String);
+            if (hasSha256Certificate(sha256Bytes, flags)) {
+                return true;
+            }
+
+            // Not in signing history, either represents multiple signatures or not a match.
+            // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+            // We already check the single-signer case above as part of hasSha256Certificate, so no
+            // need to verify we have multiple signers, just run the old check
+            // just consider current signing certs
+            final String[] mSignaturesSha256Digests =
+                    PackageUtils.computeSignaturesSha256Digests(signatures);
+            final String mSignaturesSha256Digest =
+                    PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+            return mSignaturesSha256Digest.equals(sha256String);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+         * history, including the current signer.  Automatically returns false if this object has
+         * multiple signing certificates, since rotation is only supported for single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate) {
+            return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+         * certificate in this SigningDetails' signing certificate history, including the current
+         * signer, and whether or not it has the given permission.  Certificates which match our
+         * current signer automatically get all capabilities. Automatically returns false if this
+         * object has multiple signing certificates, since rotation is only supported for
+         * single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+            return hasSha256CertificateInternal(sha256Certificate, flags);
+        }
+
+        private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the last one, which automatically gets all
+                // capabilities, since it is the same as the current signature, and is checked below
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    byte[] digest = PackageUtils.computeSha256DigestBytes(
+                            pastSigningCertificates[i].toByteArray());
+                    if (Arrays.equals(sha256Certificate, digest)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer
+            if (signatures.length == 1) {
+                byte[] digest =
+                        PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+                return Arrays.equals(sha256Certificate, digest);
+            }
+            return false;
+        }
+
         /** Returns true if the signatures in this and other match exactly. */
         public boolean signaturesMatchExactly(SigningDetails other) {
             return Signature.areExactMatch(this.signatures, other.signatures);
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index fdc54ae..a2a14ed 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -285,6 +285,29 @@
     }
 
     /**
+     * Test if given {@link Signature} objects are effectively equal. In rare
+     * cases, certificates can have slightly malformed encoding which causes
+     * exact-byte checks to fail.
+     * <p>
+     * To identify effective equality, we bounce the certificates through an
+     * decode/encode pass before doing the exact-byte check. To reduce attack
+     * surface area, we only allow a byte size delta of a few bytes.
+     *
+     * @throws CertificateException if the before/after length differs
+     *             substantially, usually a signal of something fishy going on.
+     * @hide
+     */
+    public static boolean areEffectiveMatch(Signature a, Signature b)
+            throws CertificateException {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        final Signature aPrime = bounce(cf, a);
+        final Signature bPrime = bounce(cf, b);
+
+        return aPrime.equals(bPrime);
+    }
+
+    /**
      * Bounce the given {@link Signature} through a decode/encode cycle.
      *
      * @throws CertificateException if the before/after length differs
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index f47cd66..eb4bced 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -576,7 +576,7 @@
      *
      * @see #enableSurfaceSharing
      */
-    public static int getMaxSharedSurfaceCount() {
+    public int getMaxSharedSurfaceCount() {
         return MAX_SURFACES_COUNT;
     }
 
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
new file mode 100644
index 0000000..9070777
--- /dev/null
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable AmbientBrightnessDayStats;
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
new file mode 100644
index 0000000..00f3c36
--- /dev/null
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+/**
+ * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
+ * {@see DisplayManager.getAmbientBrightnessStats()}
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
+
+    /** The localdate for which brightness stats are being tracked */
+    private final LocalDate mLocalDate;
+
+    /** Ambient brightness values for creating bucket boundaries from */
+    private final float[] mBucketBoundaries;
+
+    /** Stats of how much time (in seconds) was spent in each of the buckets */
+    private final float[] mStats;
+
+    /**
+     * @hide
+     */
+    public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+            @NonNull float[] bucketBoundaries) {
+        this(localDate, bucketBoundaries, null);
+    }
+
+    /**
+     * @hide
+     */
+    public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+            @NonNull float[] bucketBoundaries, float[] stats) {
+        Preconditions.checkNotNull(localDate);
+        Preconditions.checkNotNull(bucketBoundaries);
+        Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+                "bucketBoundaries");
+        if (bucketBoundaries.length < 1) {
+            throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
+        }
+        checkSorted(bucketBoundaries);
+        if (stats == null) {
+            stats = new float[bucketBoundaries.length];
+        } else {
+            Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+            if (bucketBoundaries.length != stats.length) {
+                throw new IllegalArgumentException(
+                        "Bucket boundaries and stats must be of same size.");
+            }
+        }
+        mLocalDate = localDate;
+        mBucketBoundaries = bucketBoundaries;
+        mStats = stats;
+    }
+
+    public LocalDate getLocalDate() {
+        return mLocalDate;
+    }
+
+    public float[] getStats() {
+        return mStats;
+    }
+
+    public float[] getBucketBoundaries() {
+        return mBucketBoundaries;
+    }
+
+    private AmbientBrightnessDayStats(Parcel source) {
+        mLocalDate = LocalDate.parse(source.readString());
+        mBucketBoundaries = source.createFloatArray();
+        mStats = source.createFloatArray();
+    }
+
+    public static final Creator<AmbientBrightnessDayStats> CREATOR =
+            new Creator<AmbientBrightnessDayStats>() {
+
+                @Override
+                public AmbientBrightnessDayStats createFromParcel(Parcel source) {
+                    return new AmbientBrightnessDayStats(source);
+                }
+
+                @Override
+                public AmbientBrightnessDayStats[] newArray(int size) {
+                    return new AmbientBrightnessDayStats[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
+        return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
+                other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = result * prime + mLocalDate.hashCode();
+        result = result * prime + Arrays.hashCode(mBucketBoundaries);
+        result = result * prime + Arrays.hashCode(mStats);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder bucketBoundariesString = new StringBuilder();
+        StringBuilder statsString = new StringBuilder();
+        for (int i = 0; i < mBucketBoundaries.length; i++) {
+            if (i != 0) {
+                bucketBoundariesString.append(", ");
+                statsString.append(", ");
+            }
+            bucketBoundariesString.append(mBucketBoundaries[i]);
+            statsString.append(mStats[i]);
+        }
+        return new StringBuilder()
+                .append(mLocalDate).append(" ")
+                .append("{").append(bucketBoundariesString).append("} ")
+                .append("{").append(statsString).append("}").toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mLocalDate.toString());
+        dest.writeFloatArray(mBucketBoundaries);
+        dest.writeFloatArray(mStats);
+    }
+
+    /** @hide */
+    public void log(float ambientBrightness, float durationSec) {
+        int bucketIndex = getBucketIndex(ambientBrightness);
+        if (bucketIndex >= 0) {
+            mStats[bucketIndex] += durationSec;
+        }
+    }
+
+    private int getBucketIndex(float ambientBrightness) {
+        if (ambientBrightness < mBucketBoundaries[0]) {
+            return -1;
+        }
+        int low = 0;
+        int high = mBucketBoundaries.length - 1;
+        while (low < high) {
+            int mid = (low + high) / 2;
+            if (mBucketBoundaries[mid] <= ambientBrightness
+                    && ambientBrightness < mBucketBoundaries[mid + 1]) {
+                return mid;
+            } else if (mBucketBoundaries[mid] < ambientBrightness) {
+                low = mid + 1;
+            } else if (mBucketBoundaries[mid] > ambientBrightness) {
+                high = mid - 1;
+            }
+        }
+        return low;
+    }
+
+    private static void checkSorted(float[] values) {
+        if (values.length <= 1) {
+            return;
+        }
+        float prevValue = values[0];
+        for (int i = 1; i < values.length; i++) {
+            Preconditions.checkState(prevValue < values[i]);
+            prevValue = values[i];
+        }
+        return;
+    }
+}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 2301824..02eb28c 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -54,19 +54,30 @@
     /** Most recent battery level when brightness was changed or Float.NaN */
     public final float batteryLevel;
 
+    /** Factor applied to brightness due to battery level, 0.0-1.0 */
+    public final float powerBrightnessFactor;
+
     /** Color filter active to provide night mode */
     public final boolean nightMode;
 
     /** If night mode color filter is active this will be the temperature in kelvin */
     public final int colorTemperature;
 
-    /** Brightness le vel before slider adjustment */
+    /** Brightness level before slider adjustment */
     public final float lastBrightness;
 
+    /** Whether brightness configuration is default version */
+    public final boolean isDefaultBrightnessConfig;
+
+    /** Whether brightness curve includes a user brightness point */
+    public final boolean isUserSetBrightness;
+
+
     /** @hide */
     private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
             int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
-            boolean nightMode, int colorTemperature, float lastBrightness) {
+            float powerBrightnessFactor, boolean nightMode, int colorTemperature,
+            float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
         this.brightness = brightness;
         this.timeStamp = timeStamp;
         this.packageName = packageName;
@@ -74,9 +85,12 @@
         this.luxValues = luxValues;
         this.luxTimestamps = luxTimestamps;
         this.batteryLevel = batteryLevel;
+        this.powerBrightnessFactor = powerBrightnessFactor;
         this.nightMode = nightMode;
         this.colorTemperature = colorTemperature;
         this.lastBrightness = lastBrightness;
+        this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+        this.isUserSetBrightness = isUserSetBrightness;
     }
 
     /** @hide */
@@ -88,9 +102,12 @@
         this.luxValues = other.luxValues;
         this.luxTimestamps = other.luxTimestamps;
         this.batteryLevel = other.batteryLevel;
+        this.powerBrightnessFactor = other.powerBrightnessFactor;
         this.nightMode = other.nightMode;
         this.colorTemperature = other.colorTemperature;
         this.lastBrightness = other.lastBrightness;
+        this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
+        this.isUserSetBrightness = other.isUserSetBrightness;
     }
 
     private BrightnessChangeEvent(Parcel source) {
@@ -101,9 +118,12 @@
         luxValues = source.createFloatArray();
         luxTimestamps = source.createLongArray();
         batteryLevel = source.readFloat();
+        powerBrightnessFactor = source.readFloat();
         nightMode = source.readBoolean();
         colorTemperature = source.readInt();
         lastBrightness = source.readFloat();
+        isDefaultBrightnessConfig = source.readBoolean();
+        isUserSetBrightness = source.readBoolean();
     }
 
     public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -130,9 +150,12 @@
         dest.writeFloatArray(luxValues);
         dest.writeLongArray(luxTimestamps);
         dest.writeFloat(batteryLevel);
+        dest.writeFloat(powerBrightnessFactor);
         dest.writeBoolean(nightMode);
         dest.writeInt(colorTemperature);
         dest.writeFloat(lastBrightness);
+        dest.writeBoolean(isDefaultBrightnessConfig);
+        dest.writeBoolean(isUserSetBrightness);
     }
 
     /** @hide */
@@ -144,9 +167,12 @@
         private float[] mLuxValues;
         private long[] mLuxTimestamps;
         private float mBatteryLevel;
+        private float mPowerBrightnessFactor;
         private boolean mNightMode;
         private int mColorTemperature;
         private float mLastBrightness;
+        private boolean mIsDefaultBrightnessConfig;
+        private boolean mIsUserSetBrightness;
 
         /** {@see BrightnessChangeEvent#brightness} */
         public Builder setBrightness(float brightness) {
@@ -190,6 +216,12 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#powerSaveBrightness} */
+        public Builder setPowerBrightnessFactor(float powerBrightnessFactor) {
+            mPowerBrightnessFactor = powerBrightnessFactor;
+            return this;
+        }
+
         /** {@see BrightnessChangeEvent#nightMode} */
         public Builder setNightMode(boolean nightMode) {
             mNightMode = nightMode;
@@ -208,11 +240,24 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */
+        public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) {
+            mIsDefaultBrightnessConfig = isDefaultBrightnessConfig;
+            return this;
+        }
+
+        /** {@see BrightnessChangeEvent#userBrightnessPoint} */
+        public Builder setUserBrightnessPoint(boolean isUserSetBrightness) {
+            mIsUserSetBrightness = isUserSetBrightness;
+            return this;
+        }
+
         /** Builds a BrightnessChangeEvent */
         public BrightnessChangeEvent build() {
             return new BrightnessChangeEvent(mBrightness, mTimeStamp,
                     mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
-                    mNightMode, mColorTemperature, mLastBrightness);
+                    mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+                    mIsDefaultBrightnessConfig, mIsUserSetBrightness);
         }
     }
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4de4880..22fb8e7 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -631,6 +631,16 @@
     }
 
     /**
+     * Fetch {@link AmbientBrightnessDayStats}s.
+     *
+     * @hide until we make it a system api
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+        return mGlobal.getAmbientBrightnessStats();
+    }
+
+    /**
      * Sets the global display brightness configuration.
      *
      * @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 2d5f5e0..d7f7c86 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -525,6 +525,21 @@
         }
     }
 
+    /**
+     * Retrieves ambient brightness stats.
+     */
+    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+        try {
+            ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
+            if (stats == null) {
+                return Collections.emptyList();
+            }
+            return stats.getList();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
         @Override
         public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 1cfad4f..f468942 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -174,9 +174,9 @@
     public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
 
     /**
-     * Persist brightness slider events.
+     * Persist brightness slider events and ambient brightness stats.
      */
-    public abstract void persistBrightnessSliderEvents();
+    public abstract void persistBrightnessTrackerState();
 
     /**
      * Notifies the display manager that resource overlays have changed.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 13599cf..0571ae1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -87,6 +87,9 @@
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
     ParceledListSlice getBrightnessEvents(String callingPackage);
 
+    // Requires ACCESS_AMBIENT_LIGHT_STATS permission.
+    ParceledListSlice getAmbientBrightnessStats();
+
     // Sets the global brightness configuration for a given user. Requires
     // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
     // the same as the calling user.
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 398dda1..91bbdc7 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -88,18 +88,22 @@
     /* Returns true if the specified USB function is enabled. */
     boolean isFunctionEnabled(String function);
 
-    /* Sets the current USB function as well as whether USB data
-     * (for example, MTP exposed pictures) should be made available
-     * on the USB connection. Unlocking data should only be done with
-     * user involvement, since exposing pictures or other data could
-     * leak sensitive user information.
-     */
+    /* Sets the current USB function. */
+    void setCurrentFunctions(long functions);
+
+    /* Compatibility version of setCurrentFunctions(long). */
     void setCurrentFunction(String function, boolean usbDataUnlocked);
 
+    /* Gets the current USB functions. */
+    long getCurrentFunctions();
+
     /* Sets the screen unlocked USB function(s), which will be set automatically
      * when the screen is unlocked.
      */
-    void setScreenUnlockedFunctions(String function);
+    void setScreenUnlockedFunctions(long functions);
+
+    /* Gets the current screen unlocked functions. */
+    long getScreenUnlockedFunctions();
 
     /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
      * the public key to list of host keys that the user has approved.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 7617c2b..8daecac 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -28,6 +28,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -37,6 +38,8 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
 
 /**
  * This class allows you to access the state of USB and communicate with USB devices.
@@ -70,7 +73,7 @@
      * MTP function is enabled
      * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
      * PTP function is enabled
-     * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+     * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the
      * accessory function is enabled
      * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
      * audio source function is enabled
@@ -196,8 +199,7 @@
 
     /**
      * A placeholder indicating that no USB function is being specified.
-     * Used to distinguish between selecting no function vs. the default function in
-     * {@link #setCurrentFunction(String)}.
+     * Used for compatibility with old init scripts to indicate no functions vs. charging function.
      *
      * {@hide}
      */
@@ -298,6 +300,69 @@
      */
     public static final String EXTRA_PERMISSION_GRANTED = "permission";
 
+    /**
+     * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_NONE = 0;
+
+    /**
+     * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_MTP = GadgetFunction.MTP;
+
+    /**
+     * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_PTP = GadgetFunction.PTP;
+
+    /**
+     * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
+
+    /**
+     * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
+
+    /**
+     * Code for the accessory usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
+
+    /**
+     * Code for the audio source usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+
+    /**
+     * Code for the adb usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_ADB = GadgetFunction.ADB;
+
+    private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
+            | FUNCTION_MIDI;
+
+    private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
+
+    static {
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
+    }
+
     private final Context mContext;
     private final IUsbManager mService;
 
@@ -548,15 +613,14 @@
      * services offered by the device.
      * </p>
      *
+     * @deprecated use getCurrentFunctions() instead.
      * @param function name of the USB function
      * @return true if the USB function is enabled
      *
      * {@hide}
      */
+    @Deprecated
     public boolean isFunctionEnabled(String function) {
-        if (mService == null) {
-            return false;
-        }
         try {
             return mService.isFunctionEnabled(function);
         } catch (RemoteException e) {
@@ -565,7 +629,7 @@
     }
 
     /**
-     * Sets the current USB function when in device mode.
+     * Sets the current USB functions when in device mode.
      * <p>
      * USB functions represent interfaces which are published to the host to access
      * services offered by the device.
@@ -574,27 +638,59 @@
      * automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
      * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
      * </p><p>
-     * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
-     * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
-     * or {@link #USB_FUNCTION_RNDIS}.
-     * </p><p>
-     * Also sets whether USB data (for example, MTP exposed pictures) should be made available
-     * on the USB connection when in device mode. Unlocking usb data should only be done with
-     * user involvement, since exposing pictures or other data could leak sensitive
-     * user information.
+     * An argument of 0 indicates that the device is charging, and can pick any
+     * appropriate function for that purpose.
      * </p><p>
      * Note: This function is asynchronous and may fail silently without applying
      * the requested changes.
      * </p>
      *
-     * @param function name of the USB function, or null to restore the default function
-     * @param usbDataUnlocked whether user data is accessible
+     * @param functions the USB function(s) to set, as a bitwise mask.
+     *                  Must satisfy {@link UsbManager#areSettableFunctions}
      *
      * {@hide}
      */
-    public void setCurrentFunction(String function, boolean usbDataUnlocked) {
+    public void setCurrentFunctions(long functions) {
         try {
-            mService.setCurrentFunction(function, usbDataUnlocked);
+            mService.setCurrentFunctions(functions);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the current USB functions when in device mode.
+     *
+     * @deprecated use setCurrentFunctions(long) instead.
+     * @param functions the USB function(s) to set.
+     * @param usbDataUnlocked unused
+
+     * {@hide}
+     */
+    @Deprecated
+    public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+        try {
+            mService.setCurrentFunction(functions, usbDataUnlocked);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current USB functions in device mode.
+     * <p>
+     * This function returns the state of primary USB functions and can return a
+     * mask containing any usb function(s) except for ADB.
+     * </p>
+     *
+     * @return The currently enabled functions, in a bitwise mask.
+     * A zero mask indicates that the current function is the charging function.
+     *
+     * {@hide}
+     */
+    public long getCurrentFunctions() {
+        try {
+            return mService.getCurrentFunctions();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -604,23 +700,37 @@
      * Sets the screen unlocked functions, which are persisted and set as the current functions
      * whenever the screen is unlocked.
      * <p>
-     * The allowed values are: {@link #USB_FUNCTION_NONE},
-     * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
-     * or {@link #USB_FUNCTION_RNDIS}.
-     * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+     * A zero mask has the effect of switching off this feature, so functions
      * no longer change on screen unlock.
      * </p><p>
      * Note: When the screen is on, this method will apply given functions as current functions,
      * which is asynchronous and may fail silently without applying the requested changes.
      * </p>
      *
-     * @param function function to set as default
+     * @param functions functions to set, in a bitwise mask.
+     *                  Must satisfy {@link UsbManager#areSettableFunctions}
      *
      * {@hide}
      */
-    public void setScreenUnlockedFunctions(String function) {
+    public void setScreenUnlockedFunctions(long functions) {
         try {
-            mService.setScreenUnlockedFunctions(function);
+            mService.setScreenUnlockedFunctions(functions);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the current screen unlocked functions.
+     *
+     * @return The currently set screen enabled functions.
+     * A zero mask indicates that the screen unlocked functions feature is not enabled.
+     *
+     * {@hide}
+     */
+    public long getScreenUnlockedFunctions() {
+        try {
+            return mService.getScreenUnlockedFunctions();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -719,51 +829,71 @@
         }
     }
 
-    /** @hide */
-    public static String addFunction(String functions, String function) {
-        if (USB_FUNCTION_NONE.equals(functions)) {
-            return function;
-        }
-        if (!containsFunction(functions, function)) {
-            if (functions.length() > 0) {
-                functions += ",";
-            }
-            functions += function;
-        }
-        return functions;
+    /**
+     * Returns whether the given functions are valid inputs to UsbManager.
+     * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+     *
+     * @return Whether the mask is settable.
+     *
+     * {@hide}
+     */
+    public static boolean areSettableFunctions(long functions) {
+        return functions == FUNCTION_NONE
+                || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
     }
 
-    /** @hide */
-    public static String removeFunction(String functions, String function) {
-        String[] split = functions.split(",");
-        for (int i = 0; i < split.length; i++) {
-            if (function.equals(split[i])) {
-                split[i] = null;
-            }
+    /**
+     * Converts the given function mask to string. Maintains ordering with respect to init scripts.
+     *
+     * @return String representation of given mask
+     *
+     * {@hide}
+     */
+    public static String usbFunctionsToString(long functions) {
+        StringJoiner joiner = new StringJoiner(",");
+        if ((functions & FUNCTION_MTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MTP);
         }
-        if (split.length == 1 && split[0] == null) {
-            return USB_FUNCTION_NONE;
+        if ((functions & FUNCTION_PTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_PTP);
         }
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < split.length; i++) {
-            String s = split[i];
-            if (s != null) {
-                if (builder.length() > 0) {
-                    builder.append(",");
-                }
-                builder.append(s);
-            }
+        if ((functions & FUNCTION_RNDIS) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_RNDIS);
         }
-        return builder.toString();
+        if ((functions & FUNCTION_MIDI) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MIDI);
+        }
+        if ((functions & FUNCTION_ACCESSORY) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+        }
+        if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+        }
+        if ((functions & FUNCTION_ADB) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ADB);
+        }
+        return joiner.toString();
     }
 
-    /** @hide */
-    public static boolean containsFunction(String functions, String function) {
-        int index = functions.indexOf(function);
-        if (index < 0) return false;
-        if (index > 0 && functions.charAt(index - 1) != ',') return false;
-        int charAfter = index + function.length();
-        if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
-        return true;
+    /**
+     * Parses a string of usb functions that are comma separated.
+     *
+     * @return A mask of all valid functions in the string
+     *
+     * {@hide}
+     */
+    public static long usbFunctionsFromString(String functions) {
+        if (functions == null || functions.equals(USB_FUNCTION_NONE)) {
+            return FUNCTION_NONE;
+        }
+        long ret = 0;
+        for (String function : functions.split(",")) {
+            if (FUNCTION_NAME_TO_CODE.containsKey(function)) {
+                ret |= FUNCTION_NAME_TO_CODE.get(function);
+            } else if (function.length() > 0) {
+                throw new IllegalArgumentException("Invalid usb function " + functions);
+            }
+        }
+        return ret;
     }
 }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 9ccdbe2..0829b4a 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -462,7 +462,7 @@
             mConfig.setMode(MODE_TUNNEL);
             mConfig.setSourceAddress(sourceAddress.getHostAddress());
             mConfig.setSpiResourceId(spi.getResourceId());
-            return new IpSecTransform(mContext, mConfig);
+            return new IpSecTransform(mContext, mConfig).activate();
         }
 
         /**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index eb264d6d..4aadc5b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -21,7 +21,9 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
+import com.android.internal.os.BinderInternal;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
@@ -934,6 +936,7 @@
                     final int totalUnclearedSize = unclearedSize();
                     if (totalUnclearedSize >= CRASH_AT_SIZE) {
                         dumpProxyInterfaceCounts();
+                        dumpPerUidProxyCounts();
                         Runtime.getRuntime().gc();
                         throw new AssertionError("Binder ProxyMap has too many entries: "
                                 + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
@@ -987,6 +990,20 @@
             }
         }
 
+        /**
+         * Dump per uid binder proxy counts to the logcat.
+         */
+        private void dumpPerUidProxyCounts() {
+            SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+            if (counts.size() == 0) return;
+            Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+            for (int i = 0; i < counts.size(); i++) {
+                final int uid = counts.keyAt(i);
+                final int binderCount = counts.valueAt(i);
+                Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
+            }
+        }
+
         // Corresponding ArrayLists in the following two arrays always have the same size.
         // They contain no empty entries. However WeakReferences in the values ArrayLists
         // may have been cleared.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c53ec1..21d245d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,11 @@
 
 package android.os;
 
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.provider.DocumentsContract.Document;
@@ -29,6 +34,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
 import java.io.BufferedInputStream;
@@ -41,10 +47,12 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
@@ -81,6 +89,14 @@
 
     private static final File[] EMPTY = new File[0];
 
+    private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+    private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+    public interface CopyListener {
+        public void onProgress(long progress);
+    }
+
     /**
      * Set owner and mode of of given {@link File}.
      *
@@ -185,6 +201,9 @@
         return false;
     }
 
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     */
     @Deprecated
     public static boolean copyFile(File srcFile, File destFile) {
         try {
@@ -195,14 +214,19 @@
         }
     }
 
-    // copy a file from srcFile to destFile, return true if succeed, return
-    // false if fail
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     */
+    @Deprecated
     public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
         try (InputStream in = new FileInputStream(srcFile)) {
             copyToFileOrThrow(in, destFile);
         }
     }
 
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     */
     @Deprecated
     public static boolean copyToFile(InputStream inputStream, File destFile) {
         try {
@@ -214,28 +238,153 @@
     }
 
     /**
-     * Copy data from a source stream to destFile.
-     * Return true if succeed, return false if failed.
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
      */
-    public static void copyToFileOrThrow(InputStream inputStream, File destFile)
-            throws IOException {
+    @Deprecated
+    public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
         if (destFile.exists()) {
             destFile.delete();
         }
-        FileOutputStream out = new FileOutputStream(destFile);
-        try {
-            byte[] buffer = new byte[4096];
-            int bytesRead;
-            while ((bytesRead = inputStream.read(buffer)) >= 0) {
-                out.write(buffer, 0, bytesRead);
-            }
-        } finally {
-            out.flush();
+        try (FileOutputStream out = new FileOutputStream(destFile)) {
+            copy(in, out);
             try {
-                out.getFD().sync();
-            } catch (IOException e) {
+                Os.fsync(out.getFD());
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
             }
-            out.close();
+        }
+    }
+
+    public static void copy(File from, File to) throws IOException {
+        try (FileInputStream in = new FileInputStream(from);
+                FileOutputStream out = new FileOutputStream(to)) {
+            copy(in, out);
+        }
+    }
+
+    public static void copy(InputStream in, OutputStream out) throws IOException {
+        copy(in, out, null, null);
+    }
+
+    public static void copy(InputStream in, OutputStream out, CopyListener listener,
+            CancellationSignal signal) throws IOException {
+        if (ENABLE_COPY_OPTIMIZATIONS) {
+            if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+                copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+                        listener, signal);
+            }
+        }
+
+        // Worse case fallback to userspace
+        copyInternalUserspace(in, out, listener, signal);
+    }
+
+    public static void copy(FileDescriptor in, FileDescriptor out) throws IOException {
+        copy(in, out, null, null);
+    }
+
+    public static void copy(FileDescriptor in, FileDescriptor out, CopyListener listener,
+            CancellationSignal signal) throws IOException {
+        if (ENABLE_COPY_OPTIMIZATIONS) {
+            try {
+                final StructStat st_in = Os.fstat(in);
+                final StructStat st_out = Os.fstat(out);
+                if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+                    copyInternalSendfile(in, out, listener, signal);
+                } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+                    copyInternalSplice(in, out, listener, signal);
+                }
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        }
+
+        // Worse case fallback to userspace
+        copyInternalUserspace(in, out, listener, signal);
+    }
+
+    /**
+     * Requires one of input or output to be a pipe.
+     */
+    @VisibleForTesting
+    public static void copyInternalSplice(FileDescriptor in, FileDescriptor out,
+            CopyListener listener, CancellationSignal signal) throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.splice(in, null, out, null, COPY_CHECKPOINT_BYTES,
+                SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+            progress += t;
+            checkpoint += t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
+        }
+    }
+
+    /**
+     * Requires both input and output to be a regular file.
+     */
+    @VisibleForTesting
+    public static void copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+            CopyListener listener, CancellationSignal signal) throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.sendfile(out, in, null, COPY_CHECKPOINT_BYTES)) != 0) {
+            progress += t;
+            checkpoint += t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public static void copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+            CopyListener listener, CancellationSignal signal) throws IOException {
+        copyInternalUserspace(new FileInputStream(in), new FileOutputStream(out), listener, signal);
+    }
+
+    @VisibleForTesting
+    public static void copyInternalUserspace(InputStream in, OutputStream out,
+            CopyListener listener, CancellationSignal signal) throws IOException {
+        long progress = 0;
+        long checkpoint = 0;
+        byte[] buffer = new byte[8192];
+
+        int t;
+        while ((t = in.read(buffer)) != -1) {
+            out.write(buffer, 0, t);
+
+            progress += t;
+            checkpoint += t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
         }
     }
 
@@ -797,4 +946,69 @@
         }
         return val * pow;
     }
+
+    @VisibleForTesting
+    public static class MemoryPipe extends Thread implements AutoCloseable {
+        private final FileDescriptor[] pipe;
+        private final byte[] data;
+        private final boolean sink;
+
+        private MemoryPipe(byte[] data, boolean sink) throws IOException {
+            try {
+                this.pipe = Os.pipe();
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+            this.data = data;
+            this.sink = sink;
+        }
+
+        private MemoryPipe startInternal() {
+            super.start();
+            return this;
+        }
+
+        public static MemoryPipe createSource(byte[] data) throws IOException {
+            return new MemoryPipe(data, false).startInternal();
+        }
+
+        public static MemoryPipe createSink(byte[] data) throws IOException {
+            return new MemoryPipe(data, true).startInternal();
+        }
+
+        public FileDescriptor getFD() {
+            return sink ? pipe[1] : pipe[0];
+        }
+
+        public FileDescriptor getInternalFD() {
+            return sink ? pipe[0] : pipe[1];
+        }
+
+        @Override
+        public void run() {
+            final FileDescriptor fd = getInternalFD();
+            try {
+                int i = 0;
+                while (i < data.length) {
+                    if (sink) {
+                        i += Os.read(fd, data, i, data.length - i);
+                    } else {
+                        i += Os.write(fd, data, i, data.length - i);
+                    }
+                }
+            } catch (IOException | ErrnoException e) {
+                throw new RuntimeException(e);
+            } finally {
+                if (sink) {
+                    SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+                }
+                IoUtils.closeQuietly(fd);
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            IoUtils.closeQuietly(getFD());
+        }
+    }
 }
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 2a088a6..cdee110 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -87,6 +87,9 @@
      * Configures how many threads the process-wide hwbinder threadpool
      * has to process incoming requests.
      *
+     * @param maxThreads total number of threads to create (includes this thread if
+     *     callerWillJoin is true)
+     * @param callerWillJoin whether joinRpcThreadpool will be called in advance
      * @hide
      */
     @SystemApi
@@ -125,6 +128,12 @@
 
     /**
      * Enable instrumentation if available.
+     *
+     * On a non-user build, this method:
+     * - tries to enable atracing (if enabled)
+     * - tries to enable coverage dumps (if running in VTS)
+     * - tries to enable record and replay (if running in VTS)
+     *
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 0c592e1..a565dee 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -30,6 +30,11 @@
     /**
      * Process a hwbinder transaction.
      *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     *
      * @hide
      */
     @SystemApi
@@ -39,6 +44,7 @@
 
     /**
      * Return as IHwInterface instance only if this implements descriptor.
+     *
      * @param descriptor for example foo.bar@1.0::IBaz
      * @hide
      */
@@ -53,6 +59,8 @@
     public interface DeathRecipient {
         /**
          * Callback for a registered process dying.
+         *
+         * @param cookie cookie this death recipient was registered with.
          */
         @SystemApi
         public void serviceDied(long cookie);
@@ -61,11 +69,16 @@
     /**
      * Notifies the death recipient with the cookie when the process containing
      * this binder dies.
+     *
+     * @param recipient callback object to be called on object death.
+     * @param cookie value to be given to callback on object death.
      */
     @SystemApi
     public boolean linkToDeath(DeathRecipient recipient, long cookie);
     /**
      * Unregisters the death recipient from this binder.
+     *
+     * @param recipient callback to no longer recieve death notifications on this binder.
      */
     @SystemApi
     public boolean unlinkToDeath(DeathRecipient recipient);
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index a2f59a9..1d9e2b0 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -21,7 +21,7 @@
 @SystemApi
 public interface IHwInterface {
     /**
-     * Returns the binder object that corresponds to an interface.
+     * @return the binder object that corresponds to this interface.
      */
     @SystemApi
     public IHwBinder asBinder();
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 1336c66..9b6d6e5 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -33,10 +33,12 @@
 @TestApi
 @SystemService(Context.INCIDENT_SERVICE)
 public class IncidentManager {
-    private static final String TAG = "incident";
+    private static final String TAG = "IncidentManager";
 
     private final Context mContext;
 
+    private IIncidentManager mService;
+
     /**
      * @hide
      */
@@ -96,19 +98,45 @@
         reportIncidentInternal(args);
     }
 
-    private void reportIncidentInternal(IncidentReportArgs args) {
-        final IIncidentManager service = IIncidentManager.Stub.asInterface(
-                ServiceManager.getService(Context.INCIDENT_SERVICE));
-        if (service == null) {
-            Slog.e(TAG, "reportIncident can't find incident binder service");
-            return;
+    private class IncidentdDeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            synchronized (this) {
+                mService = null;
+            }
         }
+    }
 
+    private void reportIncidentInternal(IncidentReportArgs args) {
         try {
+            final IIncidentManager service = getIIncidentManagerLocked();
+            if (service == null) {
+                Slog.e(TAG, "reportIncident can't find incident binder service");
+                return;
+            }
             service.reportIncident(args);
         } catch (RemoteException ex) {
             Slog.e(TAG, "reportIncident failed", ex);
         }
     }
+
+    private IIncidentManager getIIncidentManagerLocked() throws RemoteException {
+        if (mService != null) {
+            return mService;
+        }
+
+        synchronized (this) {
+            if (mService != null) {
+                return mService;
+            }
+            mService = IIncidentManager.Stub.asInterface(
+                ServiceManager.getService(Context.INCIDENT_SERVICE));
+            if (mService != null) {
+                mService.asBinder().linkToDeath(new IncidentdDeathRecipient(), 0);
+            }
+            return mService;
+        }
+    }
+
 }
 
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index fd0ebcf..9fa129c 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -32,6 +32,9 @@
 @TestApi
 public final class IncidentReportArgs implements Parcelable {
 
+    private static final int DEST_EXPLICIT = 100;
+    private static final int DEST_AUTO = 200;
+
     private final IntArray mSections = new IntArray();
     private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
     private boolean mAll;
@@ -41,6 +44,7 @@
      * Construct an incident report args with no fields.
      */
     public IncidentReportArgs() {
+        mDest = DEST_AUTO;
     }
 
     /**
@@ -143,7 +147,14 @@
      * @hide
      */
     public void setPrivacyPolicy(int dest) {
-        mDest = dest;
+        switch (dest) {
+            case DEST_EXPLICIT:
+            case DEST_AUTO:
+                mDest = dest;
+                break;
+            default:
+                mDest = DEST_AUTO;
+        }
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2440b48..ad0ce49 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1903,7 +1903,7 @@
                 cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
                 String newValue = getStringForUser(cr, name, userHandle);
                 StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
-                        makeDefault ? 1 : 0, userHandle);
+                        makeDefault, userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                 return false;
@@ -10520,6 +10520,18 @@
         public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
 
         /**
+         * Flag to keep background restricted profiles running after exiting. If disabled,
+         * the restricted profile can be put into stopped state as soon as the user leaves it.
+         * Type: int (0 for false, 1 for true)
+         *
+         * Overridden by the system based on device information. If null, the value specified
+         * by {@code config_keepRestrictedProfilesInBackground} is used.
+         *
+         * @hide
+         */
+        public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
+
+        /**
          * Get the key that retrieves a bluetooth headset's priority.
          * @hide
          */
diff --git a/core/java/android/security/ConfirmationAlreadyPresentingException.java b/core/java/android/security/ConfirmationAlreadyPresentingException.java
new file mode 100644
index 0000000..ae4ec5a
--- /dev/null
+++ b/core/java/android/security/ConfirmationAlreadyPresentingException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because another prompt is already
+ * being presented.
+ */
+public class ConfirmationAlreadyPresentingException extends Exception {
+    public ConfirmationAlreadyPresentingException() {}
+
+    public ConfirmationAlreadyPresentingException(String message) {
+        super(message);
+    }
+}
diff --git a/core/java/android/security/ConfirmationCallback.java b/core/java/android/security/ConfirmationCallback.java
new file mode 100644
index 0000000..4670bce
--- /dev/null
+++ b/core/java/android/security/ConfirmationCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback class used when signaling that a prompt is no longer being presented.
+ */
+public abstract class ConfirmationCallback {
+    /**
+     * Called when the requested prompt was accepted by the user.
+     *
+     * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a>
+     * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and
+     * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of
+     * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as
+     * CBOR byte string (type 2). Other keys may be added in the future.
+     *
+     * @param dataThatWasConfirmed the data that was confirmed, see above for the format.
+     */
+    public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {}
+
+    /**
+     * Called when the requested prompt was dismissed (not accepted) by the user.
+     */
+    public void onDismissedByUser() {}
+
+    /**
+     * Called when the requested prompt was dismissed by the application.
+     */
+    public void onDismissedByApplication() {}
+
+    /**
+     * Called when the requested prompt was dismissed because of a low-level error.
+     *
+     * @param e an exception representing the error.
+     */
+    public void onError(Exception e) {}
+}
diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java
new file mode 100644
index 0000000..e9df370
--- /dev/null
+++ b/core/java/android/security/ConfirmationDialog.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+/**
+ * Class used for displaying confirmation prompts.
+ *
+ * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are
+ * implemented in a way that a positive response indicates with high confidence that the user has
+ * seen the given text, even if the Android framework (including the kernel) was
+ * compromised. Implementing confirmation prompts with these guarantees requires dedicated
+ * hardware-support and may not always be available.
+ *
+ * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> -
+ * in the following way. The setup steps are as follows:
+ * <ul>
+ * <li> Before first use, the application generates a key-pair with the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set. Device attestation,
+ * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to
+ * generate a certificate chain that includes the public key (<code>Kpub</code> in the following)
+ * of the newly generated key.
+ * <li> The application sends <code>Kpub</code> and the certificate chain resulting from device
+ * attestation to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root
+ * certificate is what is expected (e.g. a certificate from Google), each certificate signs the
+ * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate
+ * asserts that <code>Kpub</code> has the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set.
+ * Additionally the relying party stores <code>Kpub</code> and associates it with the device
+ * it was received from.
+ * </ul>
+ *
+ * <p>The <i>Relying Party</i> is typically an external device (for example connected via
+ * Bluetooth) or application server.
+ *
+ * <p>Before executing a transaction which requires a high assurance of user content, the
+ * application does the following:
+ * <ul>
+ * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as
+ * the <code>extraData</code> (via the Builder helper class) to the
+ * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally
+ * since it'll use it in a later step.
+ * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the
+ * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the
+ * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the
+ * user, the <code>extraData</code> parameter, and possibly other data.
+ * <li> The application signs the <i>Confirmation Response</i> with the previously created key and
+ * sends the blob and the signature to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then
+ * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the
+ * previously created nonce. If all checks passes, the transaction is executed.
+ * </ul>
+ *
+ * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the
+ * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
+ * along the nonce in the <code>extraData</code> blob.
+ */
+public class ConfirmationDialog {
+    private static final String TAG = "ConfirmationDialog";
+
+    private CharSequence mPromptText;
+    private byte[] mExtraData;
+    private ConfirmationCallback mCallback;
+    private Executor mExecutor;
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
+            ConfirmationCallback callback) {
+        switch (responseCode) {
+            case KeyStore.CONFIRMATIONUI_OK:
+                callback.onConfirmedByUser(dataThatWasConfirmed);
+                break;
+
+            case KeyStore.CONFIRMATIONUI_CANCELED:
+                callback.onDismissedByUser();
+                break;
+
+            case KeyStore.CONFIRMATIONUI_ABORTED:
+                callback.onDismissedByApplication();
+                break;
+
+            case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
+                callback.onError(new Exception("System error returned by ConfirmationUI."));
+                break;
+
+            default:
+                callback.onError(new Exception("Unexpected responseCode=" + responseCode
+                                + " from onConfirmtionPromptCompleted() callback."));
+                break;
+        }
+    }
+
+    private final android.os.IBinder mCallbackBinder =
+            new android.security.IConfirmationPromptCallback.Stub() {
+                @Override
+                public void onConfirmationPromptCompleted(
+                        int responseCode, final byte[] dataThatWasConfirmed)
+                        throws android.os.RemoteException {
+                    if (mCallback != null) {
+                        ConfirmationCallback callback = mCallback;
+                        Executor executor = mExecutor;
+                        mCallback = null;
+                        mExecutor = null;
+                        if (executor == null) {
+                            doCallback(responseCode, dataThatWasConfirmed, callback);
+                        } else {
+                            executor.execute(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        doCallback(responseCode, dataThatWasConfirmed, callback);
+                                    }
+                                });
+                        }
+                    }
+                }
+            };
+
+    /**
+     * A builder that collects arguments, to be shown on the system-provided confirmation dialog.
+     */
+    public static class Builder {
+
+        private CharSequence mPromptText;
+        private byte[] mExtraData;
+
+        /**
+         * Creates a builder for the confirmation dialog.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the prompt text for the dialog.
+         *
+         * @param promptText the text to present in the prompt.
+         * @return the builder.
+         */
+        public Builder setPromptText(CharSequence promptText) {
+            mPromptText = promptText;
+            return this;
+        }
+
+        /**
+         * Sets the extra data for the dialog.
+         *
+         * @param extraData data to include in the response data.
+         * @return the builder.
+         */
+        public Builder setExtraData(byte[] extraData) {
+            mExtraData = extraData;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder.
+         *
+         * @param context the application context
+         * @return a {@link ConfirmationDialog}
+         * @throws IllegalArgumentException if any of the required fields are not set.
+         */
+        public ConfirmationDialog build(Context context) {
+            if (TextUtils.isEmpty(mPromptText)) {
+                throw new IllegalArgumentException("prompt text must be set and non-empty");
+            }
+            if (mExtraData == null) {
+                throw new IllegalArgumentException("extraData must be set");
+            }
+            return new ConfirmationDialog(mPromptText, mExtraData);
+        }
+    }
+
+    private ConfirmationDialog(CharSequence promptText, byte[] extraData) {
+        mPromptText = promptText;
+        mExtraData = extraData;
+    }
+
+    /**
+     * Requests a confirmation prompt to be presented to the user.
+     *
+     * When the prompt is no longer being presented, one of the methods in
+     * {@link ConfirmationCallback} is called on the supplied callback object.
+     *
+     * @param executor the executor identifying the thread that will receive the callback.
+     * @param callback the callback to use when the dialog is done showing.
+     * @throws IllegalArgumentException if the prompt text is too long or malfomed.
+     * @throws ConfirmationAlreadyPresentingException if another prompt is being presented.
+     * @throws ConfirmationNotAvailableException if confirmation prompts are not supported.
+     */
+    public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback)
+            throws ConfirmationAlreadyPresentingException,
+            ConfirmationNotAvailableException {
+        if (mCallback != null) {
+            throw new ConfirmationAlreadyPresentingException();
+        }
+        mCallback = callback;
+        mExecutor = executor;
+
+        int uiOptionsAsFlags = 0;
+        // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed.
+        String locale = Locale.getDefault().toLanguageTag();
+        int responseCode = mKeyStore.presentConfirmationPrompt(
+                mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
+        switch (responseCode) {
+            case KeyStore.CONFIRMATIONUI_OK:
+                return;
+
+            case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
+                throw new ConfirmationAlreadyPresentingException();
+
+            case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
+                throw new ConfirmationNotAvailableException();
+
+            case KeyStore.CONFIRMATIONUI_UIERROR:
+                throw new IllegalArgumentException();
+
+            default:
+                // Unexpected error code.
+                Log.w(TAG,
+                        "Unexpected responseCode=" + responseCode
+                        + " from presentConfirmationPrompt() call.");
+                throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Cancels a prompt currently being displayed.
+     *
+     * On success, the
+     * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on
+     * the supplied callback object will be called asynchronously.
+     *
+     * @throws IllegalStateException if no prompt is currently being presented.
+     */
+    public void cancelPrompt() {
+        int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
+        if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
+            return;
+        } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
+            throw new IllegalStateException();
+        } else {
+            // Unexpected error code.
+            Log.w(TAG,
+                    "Unexpected responseCode=" + responseCode
+                    + " from cancelConfirmationPrompt() call.");
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Checks if the device supports confirmation prompts.
+     *
+     * @return true if confirmation prompts are supported by the device.
+     */
+    public static boolean isSupported() {
+        // TODO: read and return system property.
+        return true;
+    }
+}
diff --git a/core/java/android/security/ConfirmationNotAvailableException.java b/core/java/android/security/ConfirmationNotAvailableException.java
new file mode 100644
index 0000000..8d0e672
--- /dev/null
+++ b/core/java/android/security/ConfirmationNotAvailableException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because the the environment lacks
+ * facilities for showing confirmations.
+ */
+public class ConfirmationNotAvailableException extends Exception {
+    public ConfirmationNotAvailableException() {
+    }
+
+    public ConfirmationNotAvailableException(String message) {
+        super(message);
+    }
+}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3464370..1d13335 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -74,6 +74,7 @@
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
     public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
+    public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index a43952a..aa09f10 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -235,17 +235,7 @@
     }
 
     /**
-     * Removes secret from memory than object is no longer used.
-     * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        clearSecret();
-        super.finalize();
-    }
-
-    /**
-     * Fills mSecret with zeroes.
+     * Fills secret with zeroes.
      */
     public void clearSecret() {
         Arrays.fill(mSecret, (byte) 0);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 917efa8..12aa64e 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -468,9 +468,8 @@
  * <p>Typically, field classification can be used to detect fields that can be autofilled with
  * user data that is not associated with a specific app&mdash;such as email and physical
  * address. Once the service identifies that a such field was manually filled by the user, the
- * service could use this signal to improve its heuristics, either locally (i.e., in the same
- * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
- * be used by other users).
+ * service could use this signal to improve its heuristics on subsequent requests (for example, by
+ * infering which resource ids are associated with known fields).
  *
  * <p>The field classification workflow involves 4 steps:
  *
@@ -481,8 +480,8 @@
  *   <li>Identify which fields should be analysed by calling
  *   {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
  *   <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
- *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
- *   requests.
+ *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in
+ *   subsequent requests.
  * </ol>
  *
  * <p>The field classification is an expensive operation and should be used carefully, otherwise it
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 5c7388f..4f2f6cb 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -25,16 +25,20 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.metrics.LogMaker;
 import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+
 import java.io.IOException;
 
 /**
@@ -91,10 +95,20 @@
     private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
         // Check for permissions.
         if (!Manifest.permission.BIND_AUTOFILL_SERVICE.equals(si.permission)) {
-            Log.w(TAG, "AutofillService from '" + si.packageName + "' does not require permission "
-                    + Manifest.permission.BIND_AUTOFILL_SERVICE);
-            throw new SecurityException("Service does not require permission "
-                    + Manifest.permission.BIND_AUTOFILL_SERVICE);
+            if (Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
+                // Let it go for now...
+                Log.w(TAG, "AutofillService from '" + si.packageName + "' uses unsupported "
+                        + "permission " + Manifest.permission.BIND_AUTOFILL + ". It works for "
+                        + "now, but might not be supported on future releases");
+                new MetricsLogger().write(new LogMaker(MetricsEvent.AUTOFILL_INVALID_PERMISSION)
+                        .setPackageName(si.packageName));
+            } else {
+                Log.w(TAG, "AutofillService from '" + si.packageName
+                        + "' does not require permission "
+                        + Manifest.permission.BIND_AUTOFILL_SERVICE);
+                throw new SecurityException("Service does not require permission "
+                        + Manifest.permission.BIND_AUTOFILL_SERVICE);
+            }
         }
 
         // Get the AutoFill metadata, if declared.
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index b8e8b19..fb468a8 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -173,7 +173,10 @@
         }
 
         /**
-         * Updates the {@link RemoteViews presentation template} when a condition is satisfied.
+         * Updates the {@link RemoteViews presentation template} when a condition is satisfied by
+         * applying a series of remote view operations. This allows dynamic customization of the
+         * portion of the save UI that is controlled by the autofill service. Such dynamic
+         * customization is based on the content of target views.
          *
          * <p>The updates are applied in the sequence they are added, after the
          * {@link #addChild(int, Transformation) transformations} are applied to the children
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 266bcda..f32dee1 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -29,7 +29,6 @@
 
 import com.android.internal.util.Preconditions;
 
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.regex.Pattern;
 
@@ -99,7 +98,7 @@
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
     private final ArrayList<RemoteViews> mFieldPresentations;
-    private final ArrayList<Pattern> mFieldFilters;
+    private final ArrayList<DatasetFieldFilter> mFieldFilters;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
     @Nullable String mId;
@@ -132,7 +131,7 @@
 
     /** @hide */
     @Nullable
-    public Pattern getFilter(int index) {
+    public DatasetFieldFilter getFilter(int index) {
         return mFieldFilters.get(index);
     }
 
@@ -189,7 +188,7 @@
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
         private ArrayList<RemoteViews> mFieldPresentations;
-        private ArrayList<Pattern> mFieldFilters;
+        private ArrayList<DatasetFieldFilter> mFieldFilters;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
@@ -363,19 +362,21 @@
          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
          *        the dataset needs authentication and you have no access to the value.
-         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
          *
          * @return this builder.
          * @throws IllegalStateException if the builder was constructed without a
          *         {@link RemoteViews presentation}.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
-                @NonNull Pattern filter) {
+                @Nullable Pattern filter) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(filter, "filter cannot be null");
             Preconditions.checkState(mPresentation != null,
                     "Dataset presentation not set on constructor");
-            setLifeTheUniverseAndEverything(id, value, null, filter);
+            setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter));
             return this;
         }
 
@@ -398,23 +399,26 @@
          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
          *        the dataset needs authentication and you have no access to the value.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
          * @param presentation the presentation used to visualize this field.
-         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
          *
          * @return this builder.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
-                @NonNull Pattern filter, @NonNull RemoteViews presentation) {
+                @Nullable Pattern filter, @NonNull RemoteViews presentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(filter, "filter cannot be null");
             Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, filter);
+            setLifeTheUniverseAndEverything(id, value, presentation,
+                    new DatasetFieldFilter(filter));
             return this;
         }
 
         private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
-                @Nullable Pattern filter) {
+                @Nullable DatasetFieldFilter filter) {
             Preconditions.checkNotNull(id, "id cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
@@ -477,8 +481,8 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeTypedList(mFieldIds, flags);
         parcel.writeTypedList(mFieldValues, flags);
-        parcel.writeParcelableList(mFieldPresentations, flags);
-        parcel.writeSerializable(mFieldFilters);
+        parcel.writeTypedList(mFieldPresentations, flags);
+        parcel.writeTypedList(mFieldFilters, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeString(mId);
     }
@@ -493,22 +497,19 @@
             final Builder builder = (presentation == null)
                     ? new Builder()
                     : new Builder(presentation);
-            final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR);
+            final ArrayList<AutofillId> ids =
+                    parcel.createTypedArrayList(AutofillId.CREATOR);
             final ArrayList<AutofillValue> values =
                     parcel.createTypedArrayList(AutofillValue.CREATOR);
-            final ArrayList<RemoteViews> presentations = new ArrayList<>();
-            parcel.readParcelableList(presentations, null);
-            @SuppressWarnings("unchecked")
-            final ArrayList<Serializable> filters =
-                    (ArrayList<Serializable>) parcel.readSerializable();
-            final int idCount = (ids != null) ? ids.size() : 0;
-            final int valueCount = (values != null) ? values.size() : 0;
-            for (int i = 0; i < idCount; i++) {
+            final ArrayList<RemoteViews> presentations =
+                    parcel.createTypedArrayList(RemoteViews.CREATOR);
+            final ArrayList<DatasetFieldFilter> filters =
+                    parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+            for (int i = 0; i < ids.size(); i++) {
                 final AutofillId id = ids.get(i);
-                final AutofillValue value = (valueCount > i) ? values.get(i) : null;
-                final RemoteViews fieldPresentation = presentations.isEmpty() ? null
-                        : presentations.get(i);
-                final Pattern filter = (Pattern) filters.get(i);
+                final AutofillValue value = values.get(i);
+                final RemoteViews fieldPresentation = presentations.get(i);
+                final DatasetFieldFilter filter = filters.get(i);
                 builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
             }
             builder.setAuthentication(parcel.readParcelable(null));
@@ -521,4 +522,55 @@
             return new Dataset[size];
         }
     };
+
+    /**
+     * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a
+     * dataset field&dash; we cannot use a {@link Pattern} directly because then we wouldn't be
+     * able to differentiate whether the service explicitly passed a {@code null} filter to disable
+     * filter, or when it called the methods that does not take a filter {@link Pattern}.
+     *
+     * @hide
+     */
+    public static final class DatasetFieldFilter implements Parcelable {
+
+        @Nullable
+        public final Pattern pattern;
+
+        private DatasetFieldFilter(@Nullable Pattern pattern) {
+            this.pattern = pattern;
+        }
+
+        @Override
+        public String toString() {
+            if (!sDebug) return super.toString();
+
+            // Cannot log pattern because it could contain PII
+            return pattern == null ? "null" : pattern.pattern().length() + "_chars";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeSerializable(pattern);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Creator<DatasetFieldFilter> CREATOR =
+                new Creator<DatasetFieldFilter>() {
+
+            @Override
+            public DatasetFieldFilter createFromParcel(Parcel parcel) {
+                return new DatasetFieldFilter((Pattern) parcel.readSerializable());
+            }
+
+            @Override
+            public DatasetFieldFilter[] newArray(int size) {
+                return new DatasetFieldFilter[size];
+            }
+        };
+    }
 }
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
new file mode 100644
index 0000000..4e1425d
--- /dev/null
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.util.Preconditions;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of a field
+ * that is expected to have a {@link AutofillValue#forDate(long) date value}.
+ *
+ * <p>For example, a transformation to display a credit card expiration date as month/year would be:
+ *
+ * <pre class="prettyprint">
+ * new DateTransformation(ccExpDate, new java.text.SimpleDateFormat("MM/yyyy")
+ * </pre>
+ */
+public final class DateTransformation extends InternalTransformation implements
+        Transformation, Parcelable {
+    private static final String TAG = "DateTransformation";
+
+    private final AutofillId mFieldId;
+    private final DateFormat mDateFormat;
+
+    /**
+     * Creates a new transformation.
+     *
+     * @param id id of the screen field.
+     * @param dateFormat object used to transform the date value of the field to a String.
+     */
+    public DateTransformation(@NonNull AutofillId id, @NonNull DateFormat dateFormat) {
+        mFieldId = Preconditions.checkNotNull(id);
+        mDateFormat = Preconditions.checkNotNull(dateFormat);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    public void apply(@NonNull ValueFinder finder, @NonNull RemoteViews parentTemplate,
+            int childViewId) throws Exception {
+        final AutofillValue value = finder.findRawValueByAutofillId(mFieldId);
+        if (value == null) {
+            Log.w(TAG, "No value for id " + mFieldId);
+            return;
+        }
+        if (!value.isDate()) {
+            Log.w(TAG, "Value for " + mFieldId + " is not date: " + value);
+            return;
+        }
+
+        try {
+            final Date date = new Date(value.getDateValue());
+            final String transformed = mDateFormat.format(date);
+            if (sDebug) Log.d(TAG, "Transformed " + date + " to " + transformed);
+
+            parentTemplate.setCharSequence(childViewId, "setText", transformed);
+        } catch (Exception e) {
+            Log.w(TAG, "Could not apply " + mDateFormat + " to " + value + ": " + e);
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "DateTransformation: [id=" + mFieldId + ", format=" + mDateFormat + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mFieldId, flags);
+        parcel.writeSerializable(mDateFormat);
+    }
+
+    public static final Parcelable.Creator<DateTransformation> CREATOR =
+            new Parcelable.Creator<DateTransformation>() {
+        @Override
+        public DateTransformation createFromParcel(Parcel parcel) {
+            return new DateTransformation(parcel.readParcelable(null),
+                    (DateFormat) parcel.readSerializable());
+        }
+
+        @Override
+        public DateTransformation[] newArray(int size) {
+            return new DateTransformation[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
new file mode 100644
index 0000000..0f7b540
--- /dev/null
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Sanitizes a date {@link AutofillValue} using a {@link DateFormat}.
+ *
+ * <p>For example, to sanitize a credit card expiration date to just its month and year:
+ *
+ * <pre class="prettyprint">
+ * new DateValueSanitizer(new java.text.SimpleDateFormat("MM/yyyy");
+ * </pre>
+ */
+public final class DateValueSanitizer extends InternalSanitizer implements Sanitizer, Parcelable {
+
+    private static final String TAG = "DateValueSanitizer";
+
+    private final DateFormat mDateFormat;
+
+    /**
+     * Default constructor.
+     *
+     * @param dateFormat date format applied to the actual date value of an input field.
+      */
+    public DateValueSanitizer(@NonNull DateFormat dateFormat) {
+        mDateFormat = Preconditions.checkNotNull(dateFormat);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    @Nullable
+    public AutofillValue sanitize(@NonNull AutofillValue value) {
+        if (value == null) {
+            Log.w(TAG, "sanitize() called with null value");
+            return null;
+        }
+        if (!value.isDate()) {
+            if (sDebug) Log.d(TAG, value + " is not a date");
+            return null;
+        }
+
+        try {
+            final Date date = new Date(value.getDateValue());
+
+            // First convert it to string
+            final String converted = mDateFormat.format(date);
+            if (sDebug) Log.d(TAG, "Transformed " + date + " to " + converted);
+            // Then parse it back to date
+            final Date sanitized = mDateFormat.parse(converted);
+            if (sDebug) Log.d(TAG, "Sanitized to " + sanitized);
+            return AutofillValue.forDate(sanitized.getTime());
+        } catch (Exception e) {
+            Log.w(TAG, "Could not apply " + mDateFormat + " to " + value + ": " + e);
+            return null;
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "DateValueSanitizer: [dateFormat=" + mDateFormat + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeSerializable(mDateFormat);
+    }
+
+    public static final Parcelable.Creator<DateValueSanitizer> CREATOR =
+            new Parcelable.Creator<DateValueSanitizer>() {
+        @Override
+        public DateValueSanitizer createFromParcel(Parcel parcel) {
+            return new DateValueSanitizer((DateFormat) parcel.readSerializable());
+        }
+
+        @Override
+        public DateValueSanitizer[] newArray(int size) {
+            return new DateValueSanitizer[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 9017848..6bab6aa 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -30,6 +30,7 @@
 import android.os.Parcelable;
 import android.provider.Settings;
 import android.service.autofill.FieldClassification.Match;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.Helper;
@@ -52,12 +53,14 @@
     private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
+    private final String mId;
     private final String mAlgorithm;
     private final Bundle mAlgorithmArgs;
     private final String[] mRemoteIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
+        mId = builder.mId;
         mAlgorithm = builder.mAlgorithm;
         mAlgorithmArgs = builder.mAlgorithmArgs;
         mRemoteIds = new String[builder.mRemoteIds.size()];
@@ -75,6 +78,13 @@
         return mAlgorithm;
     }
 
+    /**
+     * Gets the id.
+     */
+    public String getId() {
+        return mId;
+    }
+
     /** @hide */
     public Bundle getAlgorithmArgs() {
         return mAlgorithmArgs;
@@ -92,6 +102,7 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("id: "); pw.print(mId);
         pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
         pw.print(" Args: "); pw.println(mAlgorithmArgs);
 
@@ -121,6 +132,7 @@
      * A builder for {@link UserData} objects.
      */
     public static final class Builder {
+        private final String mId;
         private final ArrayList<String> mRemoteIds;
         private final ArrayList<String> mValues;
         private String mAlgorithm;
@@ -131,16 +143,28 @@
          * Creates a new builder for the user data used for <a href="#FieldClassification">field
          * classification</a>.
          *
+         * <p>The user data must contain at least one pair of {@code remoteId} -> {@code value}, and
+         * more pairs can be added through the {@link #add(String, String)} method.
+         *
+         * @param id id used to identify the whole {@link UserData} object. This id is also returned
+         * by {@link AutofillManager#getUserDataId()}, which can be used to check if the
+         * {@link UserData} is up-to-date without fetching the whole object (through
+         * {@link AutofillManager#getUserData()}).
+         * @param remoteId unique string used to identify a user data value.
+         * @param value value of the user data.
+         *
          * @throws IllegalArgumentException if any of the following occurs:
          * <ol>
+         *   <li>{@code id} is empty
          *   <li>{@code remoteId} is empty
          *   <li>{@code value} is empty
          *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
          *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
          * </ol>
          */
-        public Builder(@NonNull String remoteId, @NonNull String value) {
-            checkValidRemoteId(remoteId);
+        public Builder(@NonNull String id, @NonNull String remoteId, @NonNull String value) {
+            mId = checkNotEmpty("id", id);
+            checkNotEmpty("remoteId", remoteId);
             checkValidValue(value);
             final int capacity = getMaxUserDataSize();
             mRemoteIds = new ArrayList<>(capacity);
@@ -188,7 +212,7 @@
          */
         public Builder add(@NonNull String remoteId, @NonNull String value) {
             throwIfDestroyed();
-            checkValidRemoteId(remoteId);
+            checkNotEmpty("remoteId", remoteId);
             checkValidValue(value);
 
             Preconditions.checkState(!mRemoteIds.contains(remoteId),
@@ -205,9 +229,10 @@
             return this;
         }
 
-        private void checkValidRemoteId(@Nullable String remoteId) {
-            Preconditions.checkNotNull(remoteId);
-            Preconditions.checkArgument(!remoteId.isEmpty(), "remoteId cannot be empty");
+        private String checkNotEmpty(@NonNull String name, @Nullable String value) {
+            Preconditions.checkNotNull(value);
+            Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
+            return value;
         }
 
         private void checkValidValue(@Nullable String value) {
@@ -246,7 +271,8 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("UserData: [algorithm=").append(mAlgorithm);
+        final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
+                .append(", algorithm=").append(mAlgorithm);
         // Cannot disclose remote ids or values because they could contain PII
         builder.append(", remoteIds=");
         Helper.appendRedacted(builder, mRemoteIds);
@@ -266,6 +292,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
         parcel.writeStringArray(mRemoteIds);
         parcel.writeStringArray(mValues);
         parcel.writeString(mAlgorithm);
@@ -279,9 +306,10 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
+            final String id = parcel.readString();
             final String[] remoteIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(remoteIds[0], values[0])
+            final Builder builder = new Builder(id, remoteIds[0], values[0])
                     .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
             for (int i = 1; i < remoteIds.length; i++) {
                 builder.add(remoteIds[i], values[i]);
diff --git a/core/java/android/service/autofill/ValueFinder.java b/core/java/android/service/autofill/ValueFinder.java
index 1705b7d..7f195d6 100644
--- a/core/java/android/service/autofill/ValueFinder.java
+++ b/core/java/android/service/autofill/ValueFinder.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 
 /**
  * Helper object used to obtain the value of a field in the screen being autofilled.
@@ -29,7 +30,17 @@
 public interface ValueFinder {
 
     /**
+     * Gets the value of a field as String, or {@code null} when not found.
+     */
+    @Nullable
+    default String findByAutofillId(@NonNull AutofillId id) {
+        final AutofillValue value = findRawValueByAutofillId(id);
+        return (value == null || !value.isText()) ? null : value.getTextValue().toString();
+    }
+
+    /**
      * Gets the value of a field, or {@code null} when not found.
      */
-    @Nullable String findByAutofillId(@NonNull AutofillId id);
+    @Nullable
+    AutofillValue findRawValueByAutofillId(@NonNull AutofillId id);
 }
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 11e1e67..e97f963a 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@
      */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_HAS_BUTTON,
+            FLAG_ICON_TINTABLE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {
@@ -49,6 +50,10 @@
      * Flag for suggestion type with a single button
      */
     public static final int FLAG_HAS_BUTTON = 1 << 0;
+    /**
+     * @hide
+     */
+    public static final int FLAG_ICON_TINTABLE = 1 << 1;
 
     private final String mId;
     private final CharSequence mTitle;
diff --git a/core/java/android/service/textclassifier/ITextClassificationCallback.aidl b/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
new file mode 100644
index 0000000..10bfe63
--- /dev/null
+++ b/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.view.textclassifier.TextClassification;
+
+/**
+ * Callback for a TextClassification request.
+ * @hide
+ */
+oneway interface ITextClassificationCallback {
+    void onSuccess(in TextClassification classification);
+    void onFailure();
+}
diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl
new file mode 100644
index 0000000..d2ffe34
--- /dev/null
+++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextLinksCallback;
+import android.service.textclassifier.ITextSelectionCallback;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextSelection;
+
+/**
+ * TextClassifierService binder interface.
+ * See TextClassifier for interface documentation.
+ * {@hide}
+ */
+oneway interface ITextClassifierService {
+
+    void onSuggestSelection(
+            in CharSequence text, int selectionStartIndex, int selectionEndIndex,
+            in TextSelection.Options options,
+            in ITextSelectionCallback c);
+
+    void onClassifyText(
+            in CharSequence text, int startIndex, int endIndex,
+            in TextClassification.Options options,
+            in ITextClassificationCallback c);
+
+    void onGenerateLinks(
+            in CharSequence text,
+            in TextLinks.Options options,
+            in ITextLinksCallback c);
+}
diff --git a/core/java/android/service/textclassifier/ITextLinksCallback.aidl b/core/java/android/service/textclassifier/ITextLinksCallback.aidl
new file mode 100644
index 0000000..a9e0dde
--- /dev/null
+++ b/core/java/android/service/textclassifier/ITextLinksCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.view.textclassifier.TextLinks;
+
+/**
+ * Callback for a TextLinks request.
+ * @hide
+ */
+oneway interface ITextLinksCallback {
+    void onSuccess(in TextLinks links);
+    void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/ITextSelectionCallback.aidl b/core/java/android/service/textclassifier/ITextSelectionCallback.aidl
new file mode 100644
index 0000000..1b4c4d1
--- /dev/null
+++ b/core/java/android/service/textclassifier/ITextSelectionCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.view.textclassifier.TextSelection;
+
+/**
+ * Callback for a TextSelection request.
+ * @hide
+ */
+oneway interface ITextSelectionCallback {
+    void onSuccess(in TextSelection selection);
+    void onFailure();
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
new file mode 100644
index 0000000..6c8c8bc
--- /dev/null
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextSelection;
+
+import com.android.internal.R;
+
+/**
+ * Abstract base class for the TextClassifier service.
+ *
+ * <p>A TextClassifier service provides text classification related features for the system.
+ * The system's default TextClassifierService is configured in
+ * {@code config_defaultTextClassifierService}. If this config has no value, a
+ * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
+ *
+ * <p>See: {@link TextClassifier}.
+ * See: {@link android.view.textclassifier.TextClassificationManager}.
+ *
+ * <p>Include the following in the manifest:
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourTextClassifierService"
+ *          android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
+ *     <intent-filter>
+ *         <action android:name="android.service.textclassifier.TextClassifierService" />
+ *     </intent-filter>
+ * </service>}</pre>
+ *
+ * @see TextClassifier
+ * @hide
+ */
+@SystemApi
+public abstract class TextClassifierService extends Service {
+
+    private static final String LOG_TAG = "TextClassifierService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
+     * that other applications can not abuse it.
+     */
+    @SystemApi
+    public static final String SERVICE_INTERFACE =
+            "android.service.textclassifier.TextClassifierService";
+
+    private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
+
+        // TODO(b/72533911): Implement cancellation signal
+        @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
+
+        /** {@inheritDoc} */
+        @Override
+        public void onSuggestSelection(
+                CharSequence text, int selectionStartIndex, int selectionEndIndex,
+                TextSelection.Options options, ITextSelectionCallback callback)
+                throws RemoteException {
+            TextClassifierService.this.onSuggestSelection(
+                    text, selectionStartIndex, selectionEndIndex, options, mCancellationSignal,
+                    new Callback<TextSelection>() {
+                        @Override
+                        public void onSuccess(TextSelection result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                if (callback.asBinder().isBinderAlive()) {
+                                    callback.onFailure();
+                                }
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onClassifyText(
+                CharSequence text, int startIndex, int endIndex,
+                TextClassification.Options options, ITextClassificationCallback callback)
+                throws RemoteException {
+            TextClassifierService.this.onClassifyText(
+                    text, startIndex, endIndex, options, mCancellationSignal,
+                    new Callback<TextClassification>() {
+                        @Override
+                        public void onSuccess(TextClassification result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onGenerateLinks(
+                CharSequence text, TextLinks.Options options, ITextLinksCallback callback)
+                throws RemoteException {
+            TextClassifierService.this.onGenerateLinks(
+                    text, options, mCancellationSignal,
+                    new Callback<TextLinks>() {
+                        @Override
+                        public void onSuccess(TextLinks result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+    };
+
+    @Nullable
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * Returns suggested text selection start and end indices, recognized entity types, and their
+     * associated confidence scores. The entity types are ordered from highest to lowest scoring.
+     *
+     * @param text text providing context for the selected text (which is specified
+     *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
+     * @param selectionStartIndex start index of the selected part of text
+     * @param selectionEndIndex end index of the selected part of text
+     * @param options optional input parameters
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onSuggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex,
+            @Nullable TextSelection.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextSelection> callback);
+
+    /**
+     * Classifies the specified text and returns a {@link TextClassification} object that can be
+     * used to generate a widget for handling the classified text.
+     *
+     * @param text text providing context for the text to classify (which is specified
+     *      by the sub sequence starting at startIndex and ending at endIndex)
+     * @param startIndex start index of the text to classify
+     * @param endIndex end index of the text to classify
+     * @param options optional input parameters
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onClassifyText(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int startIndex,
+            @IntRange(from = 0) int endIndex,
+            @Nullable TextClassification.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextClassification> callback);
+
+    /**
+     * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
+     * links information.
+     *
+     * @param text the text to generate annotations for
+     * @param options configuration for link generation
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onGenerateLinks(
+            @NonNull CharSequence text,
+            @Nullable TextLinks.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextLinks> callback);
+
+    /**
+     * Callbacks for TextClassifierService results.
+     *
+     * @param <T> the type of the result
+     * @hide
+     */
+    @SystemApi
+    public interface Callback<T> {
+        /**
+         * Returns the result.
+         */
+        void onSuccess(T result);
+
+        /**
+         * Signals a failure.
+         */
+        void onFailure(CharSequence error);
+    }
+
+    /**
+     * Returns the component name of the system default textclassifier service if it can be found
+     * on the system. Otherwise, returns null.
+     * @hide
+     */
+    @Nullable
+    public static ComponentName getServiceComponentName(Context context) {
+        final String str = context.getString(R.string.config_defaultTextClassifierService);
+        if (!TextUtils.isEmpty(str)) {
+            try {
+                final ComponentName componentName = ComponentName.unflattenFromString(str);
+                final Intent intent = new Intent(SERVICE_INTERFACE).setComponent(componentName);
+                final ServiceInfo si = context.getPackageManager()
+                        .getServiceInfo(intent.getComponent(), 0);
+                final String permission = si == null ? null : si.permission;
+                if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
+                    return componentName;
+                }
+                Slog.w(LOG_TAG, String.format(
+                        "Service %s should require %s permission. Found %s permission",
+                        intent.getComponent().flattenToString(),
+                        Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
+                        si.permission));
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.w(LOG_TAG, String.format("Service %s not found", str));
+            }
+        } else {
+            Slog.d(LOG_TAG, "No configured system TextClassifierService");
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index de2dcce..413cd10 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -431,7 +431,7 @@
             int c = s.charAt(i);
 
             if (c == QUOTE) {
-                count = appendQuotedText(s, i, len);
+                count = appendQuotedText(s, i);
                 len = s.length();
                 continue;
             }
@@ -574,36 +574,48 @@
                             : String.format(Locale.getDefault(), "%d", year);
     }
 
-    private static int appendQuotedText(SpannableStringBuilder s, int i, int len) {
-        if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
-            s.delete(i, i + 1);
+
+    /**
+     * Strips quotation marks from the {@code formatString} and appends the result back to the
+     * {@code formatString}.
+     *
+     * @param formatString the format string, as described in
+     *                     {@link android.text.format.DateFormat}, to be modified
+     * @param index        index of the first quote
+     * @return the length of the quoted text that was appended.
+     * @hide
+     */
+    public static int appendQuotedText(SpannableStringBuilder formatString, int index) {
+        int length = formatString.length();
+        if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) {
+            formatString.delete(index, index + 1);
             return 1;
         }
 
         int count = 0;
 
         // delete leading quote
-        s.delete(i, i + 1);
-        len--;
+        formatString.delete(index, index + 1);
+        length--;
 
-        while (i < len) {
-            char c = s.charAt(i);
+        while (index < length) {
+            char c = formatString.charAt(index);
 
             if (c == QUOTE) {
                 //  QUOTEQUOTE -> QUOTE
-                if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
+                if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) {
 
-                    s.delete(i, i + 1);
-                    len--;
+                    formatString.delete(index, index + 1);
+                    length--;
                     count++;
-                    i++;
+                    index++;
                 } else {
                     //  Closing QUOTE ends quoted text copying
-                    s.delete(i, i + 1);
+                    formatString.delete(index, index + 1);
                     break;
                 }
             } else {
-                i++;
+                index++;
                 count++;
             }
         }
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 3524179..cd199b3 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.text.Layout;
 import android.text.Spanned;
 
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
-    public DrawableMarginSpan(Drawable b) {
-        mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+    private static final int STANDARD_PAD_WIDTH = 0;
+
+    @NonNull
+    private final Drawable mDrawable;
+    @Px
+    private final int mPad;
+
+    /**
+     * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+     *
+     * @param drawable the drawable to be added
+     */
+    public DrawableMarginSpan(@NonNull Drawable drawable) {
+        this(drawable, STANDARD_PAD_WIDTH);
     }
 
-    public DrawableMarginSpan(Drawable b, int pad) {
-        mDrawable = b;
+    /**
+     * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+     *
+     * @param drawable the drawable to be added
+     * @param pad      the distance between the drawable and the text
+     */
+    public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+        mDrawable = drawable;
         mPad = pad;
     }
 
+    @Override
     public int getLeadingMargin(boolean first) {
         return mDrawable.getIntrinsicWidth() + mPad;
     }
 
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout layout) {
+    @Override
+    public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @NonNull Layout layout) {
         int st = ((Spanned) text).getSpanStart(this);
-        int ix = (int)x;
-        int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+        int ix = (int) x;
+        int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
 
         int dw = mDrawable.getIntrinsicWidth();
         int dh = mDrawable.getIntrinsicHeight();
 
         // XXX What to do about Paint?
-        mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+        mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
         mDrawable.draw(c);
     }
 
-    public void chooseHeight(CharSequence text, int start, int end,
-                             int istartv, int v,
-                             Paint.FontMetricsInt fm) {
+    @Override
+    public void chooseHeight(@NonNull CharSequence text, int start, int end,
+            int istartv, int v,
+            @NonNull Paint.FontMetricsInt fm) {
         if (end == ((Spanned) text).getSpanEnd(this)) {
             int ht = mDrawable.getIntrinsicHeight();
 
             int need = ht - (v + fm.descent - fm.ascent - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.descent += need;
+            }
 
             need = ht - (v + fm.bottom - fm.top - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.bottom += need;
+            }
         }
     }
-
-    private Drawable mDrawable;
-    private int mPad;
 }
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd..1b16f33 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -24,32 +27,71 @@
 import java.lang.ref.WeakReference;
 
 /**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
  *
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ *     mContext = context;
+ *     mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ *      Drawable drawable = mContext.getDrawable(mResourceId);
+ *      drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ *      return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
  */
 public abstract class DynamicDrawableSpan extends ReplacementSpan {
-    private static final String TAG = "DynamicDrawableSpan";
-    
+
     /**
      * A constant indicating that the bottom of this span should be aligned
      * with the bottom of the surrounding text, i.e., at the same level as the
      * lowest descender in the text.
      */
     public static final int ALIGN_BOTTOM = 0;
-    
+
     /**
      * A constant indicating that the bottom of this span should be aligned
      * with the baseline of the surrounding text.
      */
     public static final int ALIGN_BASELINE = 1;
-    
+
     protected final int mVerticalAlignment;
-    
+
+    private WeakReference<Drawable> mDrawableRef;
+
+    /**
+     * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+     * {@link #ALIGN_BOTTOM}
+     */
     public DynamicDrawableSpan() {
         mVerticalAlignment = ALIGN_BOTTOM;
     }
 
     /**
-     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+     * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+     *
+     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
      */
     protected DynamicDrawableSpan(int verticalAlignment) {
         mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@
     }
 
     /**
-     * Your subclass must implement this method to provide the bitmap   
+     * Your subclass must implement this method to provide the bitmap
      * to be drawn.  The dimensions of the bitmap must be the same
      * from each call to the next.
      */
     public abstract Drawable getDrawable();
 
     @Override
-    public int getSize(Paint paint, CharSequence text,
-                         int start, int end,
-                         Paint.FontMetricsInt fm) {
+    public int getSize(@NonNull Paint paint, CharSequence text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @Nullable Paint.FontMetricsInt fm) {
         Drawable d = getCachedDrawable();
         Rect rect = d.getBounds();
 
         if (fm != null) {
-            fm.ascent = -rect.bottom; 
-            fm.descent = 0; 
+            fm.ascent = -rect.bottom;
+            fm.descent = 0;
 
             fm.top = fm.ascent;
             fm.bottom = 0;
@@ -89,12 +131,12 @@
     }
 
     @Override
-    public void draw(Canvas canvas, CharSequence text,
-                     int start, int end, float x, 
-                     int top, int y, int bottom, Paint paint) {
+    public void draw(@NonNull Canvas canvas, CharSequence text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+            int top, int y, int bottom, @NonNull Paint paint) {
         Drawable b = getCachedDrawable();
         canvas.save();
-        
+
         int transY = bottom - b.getBounds().bottom;
         if (mVerticalAlignment == ALIGN_BASELINE) {
             transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@
         WeakReference<Drawable> wr = mDrawableRef;
         Drawable d = null;
 
-        if (wr != null)
+        if (wr != null) {
             d = wr.get();
+        }
 
         if (d == null) {
             d = getDrawable();
@@ -119,7 +162,5 @@
 
         return d;
     }
-
-    private WeakReference<Drawable> mDrawableRef;
 }
 
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index 304c83f..ad78bd5 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.text.Layout;
 import android.text.Spanned;
 
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
-    public IconMarginSpan(Bitmap b) {
-        mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+    @NonNull
+    private final Bitmap mBitmap;
+    @Px
+    private final int mPad;
+
+    /**
+     * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+     *
+     * @param bitmap bitmap to be rendered at the beginning of the text
+     */
+    public IconMarginSpan(@NonNull Bitmap bitmap) {
+        this(bitmap, 0);
     }
 
-    public IconMarginSpan(Bitmap b, int pad) {
-        mBitmap = b;
+    /**
+     * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+     *
+     * @param bitmap bitmap to be rendered at the beginning of the text
+     * @param pad    padding width, in pixels, between the bitmap and the text
+     */
+    public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+        mBitmap = bitmap;
         mPad = pad;
     }
 
+    @Override
     public int getLeadingMargin(boolean first) {
         return mBitmap.getWidth() + mPad;
     }
 
+    @Override
     public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout layout) {
+            int top, int baseline, int bottom,
+            CharSequence text, int start, int end,
+            boolean first, Layout layout) {
         int st = ((Spanned) text).getSpanStart(this);
         int itop = layout.getLineTop(layout.getLineForOffset(st));
 
-        if (dir < 0)
+        if (dir < 0) {
             x -= mBitmap.getWidth();
+        }
 
         c.drawBitmap(mBitmap, x, itop, p);
     }
 
+    @Override
     public void chooseHeight(CharSequence text, int start, int end,
-                             int istartv, int v,
-                             Paint.FontMetricsInt fm) {
+            int istartv, int v,
+            Paint.FontMetricsInt fm) {
         if (end == ((Spanned) text).getSpanEnd(this)) {
             int ht = mBitmap.getHeight();
 
             int need = ht - (v + fm.descent - fm.ascent - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.descent += need;
+            }
 
             need = ht - (v + fm.bottom - fm.top - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.bottom += need;
+            }
         }
     }
 
-    private Bitmap mBitmap;
-    private int mPad;
 }
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index b0bff68..95f0b43 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
 package android.text.style;
 
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@
 
 import java.io.InputStream;
 
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
 public class ImageSpan extends DynamicDrawableSpan {
+
+    @Nullable
     private Drawable mDrawable;
+    @Nullable
     private Uri mContentUri;
+    @DrawableRes
     private int mResourceId;
+    @Nullable
     private Context mContext;
+    @Nullable
     private String mSource;
 
     /**
      * @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
      */
     @Deprecated
-    public ImageSpan(Bitmap b) {
+    public ImageSpan(@NonNull Bitmap b) {
         this(null, b, ALIGN_BOTTOM);
     }
 
@@ -46,80 +79,143 @@
      * @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
      */
     @Deprecated
-    public ImageSpan(Bitmap b, int verticalAlignment) {
+    public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
         this(null, b, verticalAlignment);
     }
 
-    public ImageSpan(Context context, Bitmap b) {
-        this(context, b, ALIGN_BOTTOM);
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param context context used to create a drawable from {@param bitmap} based on the display
+     *                metrics of the resources
+     * @param bitmap  bitmap to be rendered
+     */
+    public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+        this(context, bitmap, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+     * alignment.
+     *
+     * @param context           context used to create a drawable from {@param bitmap} based on
+     *                          the display metrics of the resources
+     * @param bitmap            bitmap to be rendered
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mDrawable = context != null
-                ? new BitmapDrawable(context.getResources(), b)
-                : new BitmapDrawable(b);
+                ? new BitmapDrawable(context.getResources(), bitmap)
+                : new BitmapDrawable(bitmap);
         int width = mDrawable.getIntrinsicWidth();
         int height = mDrawable.getIntrinsicHeight();
-        mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0); 
-    }
-
-    public ImageSpan(Drawable d) {
-        this(d, ALIGN_BOTTOM);
+        mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
     }
 
     /**
-     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     * Constructs an {@link ImageSpan} from a drawable with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+     *
+     * @param drawable drawable to be rendered
      */
-    public ImageSpan(Drawable d, int verticalAlignment) {
-        super(verticalAlignment);
-        mDrawable = d;
-    }
-
-    public ImageSpan(Drawable d, String source) {
-        this(d, source, ALIGN_BOTTOM);
+    public ImageSpan(@NonNull Drawable drawable) {
+        this(drawable, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+     *
+     * @param drawable          drawable to be rendered
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Drawable d, String source, int verticalAlignment) {
+    public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
         super(verticalAlignment);
-        mDrawable = d;
+        mDrawable = drawable;
+    }
+
+    /**
+     * Constructs an {@link ImageSpan} from a drawable and a source with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param drawable drawable to be rendered
+     * @param source   drawable's Uri source
+     */
+    public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+        this(drawable, source, ALIGN_BOTTOM);
+    }
+
+    /**
+     * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+     *
+     * @param drawable          drawable to be rendered
+     * @param source            drawable's uri source
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
+     */
+    public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
+        super(verticalAlignment);
+        mDrawable = drawable;
         mSource = source;
     }
 
-    public ImageSpan(Context context, Uri uri) {
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+     * {@link #getSource()}
+     *
+     * @param context context used to create a drawable from {@param bitmap} based on the display
+     *                metrics of the resources
+     * @param uri     {@link Uri} used to construct the drawable that will be rendered
+     */
+    public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
         this(context, uri, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+     * alignment. The Uri source can be retrieved via {@link #getSource()}
+     *
+     * @param context           context used to create a drawable from {@param bitmap} based on
+     *                          the display
+     *                          metrics of the resources
+     * @param uri               {@link Uri} used to construct the drawable that will be rendered.
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mContentUri = uri;
         mSource = uri.toString();
     }
 
-    public ImageSpan(Context context, @DrawableRes int resourceId) {
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param context    context used to retrieve the drawable from resources
+     * @param resourceId drawable resource id based on which the drawable is retrieved
+     */
+    public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
         this(context, resourceId, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+     * alignment.
+     *
+     * @param context           context used to retrieve the drawable from resources
+     * @param resourceId        drawable resource id based on which the drawable is retrieved.
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+            int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mResourceId = resourceId;
@@ -128,10 +224,10 @@
     @Override
     public Drawable getDrawable() {
         Drawable drawable = null;
-        
+
         if (mDrawable != null) {
             drawable = mDrawable;
-        } else  if (mContentUri != null) {
+        } else if (mContentUri != null) {
             Bitmap bitmap = null;
             try {
                 InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@
                         drawable.getIntrinsicHeight());
                 is.close();
             } catch (Exception e) {
-                Log.e("sms", "Failed to loaded content " + mContentUri, e);
+                Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
             }
         } else {
             try {
@@ -150,8 +246,8 @@
                 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                         drawable.getIntrinsicHeight());
             } catch (Exception e) {
-                Log.e("sms", "Unable to find resource: " + mResourceId);
-            }                
+                Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+            }
         }
 
         return drawable;
@@ -159,9 +255,12 @@
 
     /**
      * Returns the source string that was saved during construction.
+     *
+     * @return the source string that was saved during construction
+     * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
      */
+    @Nullable
     public String getSource() {
         return mSource;
     }
-
 }
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 1ebee82..50ee5f3 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@
 import android.graphics.Paint;
 import android.text.TextPaint;
 
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+    /**
+     * Classes that implement this should define how the height is being calculated.
+     *
+     * @param text       the text
+     * @param start      the start of the line
+     * @param end        the end of the line
+     * @param spanstartv the start of the span
+     * @param lineHeight the line height
+     * @param fm         font metrics of the paint, in integers
+     */
     public void chooseHeight(CharSequence text, int start, int end,
-                             int spanstartv, int v,
-                             Paint.FontMetricsInt fm);
+            int spanstartv, int lineHeight,
+            Paint.FontMetricsInt fm);
 
+    /**
+     * The classes that affect the height of the line with respect to density, should implement this
+     * interface.
+     */
     public interface WithDensity extends LineHeightSpan {
+
+        /**
+         * Classes that implement this should define how the height is being calculated.
+         *
+         * @param text       the text
+         * @param start      the start of the line
+         * @param end        the end of the line
+         * @param spanstartv the start of the span
+         * @param lineHeight the line height
+         * @param paint      the paint
+         */
         public void chooseHeight(CharSequence text, int start, int end,
-                                 int spanstartv, int v,
-                                 Paint.FontMetricsInt fm, TextPaint paint);
+                int spanstartv, int lineHeight,
+                Paint.FontMetricsInt fm, TextPaint paint);
     }
 }
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 2ff52a8f..d76ef94 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@
 
 import android.graphics.MaskFilter;
 import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
 public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
 
     private MaskFilter mFilter;
 
+    /**
+     * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+     *
+     * @param filter the filter to be applied to the <code>TextPaint</code>
+     */
     public MaskFilterSpan(MaskFilter filter) {
         mFilter = filter;
     }
 
+    /**
+     * Return the mask filter for this span.
+     *
+     * @return the mask filter for this span
+     */
     public MaskFilter getMaskFilter() {
         return mFilter;
     }
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index f900db5..bdfa700 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.os.Parcel;
@@ -24,55 +25,76 @@
 import android.text.TextUtils;
 
 /**
- * 
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
  * Note that styles are cumulative -- if both bold and italic are set in
  * separate spans, or if the base style is bold and a span calls for italic,
  * you get bold italic.  You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
  */
 public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final int mStyle;
 
     /**
-     * 
+     * Creates a {@link StyleSpan} from a style.
+     *
      * @param style An integer constant describing the style for this span. Examples
-     * include bold, italic, and normal. Values are constants defined 
-     * in {@link android.graphics.Typeface}.
+     *              include bold, italic, and normal. Values are constants defined
+     *              in {@link Typeface}.
      */
     public StyleSpan(int style) {
         mStyle = style;
     }
 
-    public StyleSpan(Parcel src) {
+    /**
+     * Creates a {@link StyleSpan} from a parcel.
+     *
+     * @param src the parcel
+     */
+    public StyleSpan(@NonNull Parcel src) {
         mStyle = src.readInt();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.STYLE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mStyle);
     }
 
     /**
-     * Returns the style constant defined in {@link android.graphics.Typeface}. 
+     * Returns the style constant defined in {@link Typeface}.
      */
     public int getStyle() {
         return mStyle;
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 0566428..2cceb2c 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.Px;
+
 /**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
  */
-public interface TabStopSpan
-extends ParagraphStyle
-{
-    /**
-     * Returns the offset of the tab stop from the leading margin of the
-     * line.
-     * @return the offset
-     */
-    public int getTabStop();
+public interface TabStopSpan extends ParagraphStyle {
 
     /**
-     * The default implementation of TabStopSpan.
+     * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+     *
+     * @return the offset, in pixels
      */
-    public static class Standard
-    implements TabStopSpan
-    {
+    int getTabStop();
+
+    /**
+     * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+     * from the leading margin of the first line of text.
+     * <p>
+     * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+     * and we want to move the tab stop with 100px. This can be achieved like this:
+     * <pre>
+     * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+     * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+     * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+     * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+     * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
+     */
+    class Standard implements TabStopSpan {
+
+        @Px
+        private int mTabOffset;
+
         /**
-         * Constructor.
+         * Constructs a {@link TabStopSpan.Standard} based on an offset.
          *
-         * @param where the offset of the tab stop from the leading margin of
-         *        the line
+         * @param offset the offset of the tab stop from the leading margin of
+         *               the line, in pixels
          */
-        public Standard(int where) {
-            mTab = where;
+        public Standard(@IntRange(from = 0) int offset) {
+            mTabOffset = offset;
         }
 
+        @Override
         public int getTabStop() {
-            return mTab;
+            return mTabOffset;
         }
-
-        private int mTab;
     }
 }
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index aa622d8..1622812 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.os.Parcel;
@@ -24,42 +25,59 @@
 import android.text.TextUtils;
 
 /**
- * Changes the typeface family of the text to which the span is attached.
+ * Changes the typeface family of the text to which the span is attached. Examples of typeface
+ * family include "monospace", "serif", and "sans-serif".
+ * <p>
+ * For example, change the typeface of a text to "monospace" like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with typeface span");
+ * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with "monospace" typeface family.</figcaption>
  */
 public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
     private final String mFamily;
 
     /**
-     * @param family The font family for this typeface.  Examples include
-     * "monospace", "serif", and "sans-serif".
+     * Constructs a {@link TypefaceSpan} based on a font family.
+     *
+     * @param family The font family for this typeface. Examples include
+     *               "monospace", "serif", and "sans-serif".
      */
     public TypefaceSpan(String family) {
         mFamily = family;
     }
 
-    public TypefaceSpan(Parcel src) {
+    public TypefaceSpan(@NonNull Parcel src) {
         mFamily = src.readString();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.TYPEFACE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeString(mFamily);
     }
 
@@ -71,16 +89,16 @@
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
-        apply(ds, mFamily);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        apply(textPaint, mFamily);
     }
 
     @Override
-    public void updateMeasureState(TextPaint paint) {
-        apply(paint, mFamily);
+    public void updateMeasureState(@NonNull TextPaint textPaint) {
+        apply(textPaint, mFamily);
     }
 
-    private static void apply(Paint paint, String family) {
+    private static void apply(@NonNull Paint paint, String family) {
         int oldStyle;
 
         Typeface old = paint.getTypeface();
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 58239ef..eab1ef4 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -27,40 +28,71 @@
 import android.util.Log;
 import android.view.View;
 
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
 public class URLSpan extends ClickableSpan implements ParcelableSpan {
 
     private final String mURL;
 
+    /**
+     * Constructs a {@link URLSpan} from a url string.
+     *
+     * @param url the url string
+     */
     public URLSpan(String url) {
         mURL = url;
     }
 
-    public URLSpan(Parcel src) {
+    /**
+     * Constructs a {@link URLSpan} from a parcel.
+     */
+    public URLSpan(@NonNull Parcel src) {
         mURL = src.readString();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.URL_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeString(mURL);
     }
 
+    /**
+     * Get the url string for this span.
+     *
+     * @return the url string.
+     */
     public String getURL() {
         return mURL;
     }
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 768aee9..d973d4a 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UiThread;
 import android.content.Context;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
@@ -29,12 +30,16 @@
 import android.text.method.MovementMethod;
 import android.text.style.URLSpan;
 import android.util.Patterns;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
 import android.webkit.WebView;
 import android.widget.TextView;
 
 import com.android.i18n.phonenumbers.PhoneNumberMatch;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+import com.android.internal.util.Preconditions;
 
 import libcore.util.EmptyArray;
 
@@ -46,6 +51,12 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -479,6 +490,195 @@
         return hasMatches;
     }
 
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text (to avoid
+     * problems if you call it repeatedly on the same text) and sets the movement method for the
+     * TextView to LinkMovementMethod.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * @param textView TextView whose text is to be marked-up with links
+     * @param options optional parameters to specify how to generate the links
+     *
+     * @return a future that may be used to interrupt or query the background task
+     */
+    @UiThread
+    public static Future<Void> addLinksAsync(
+            @NonNull TextView textView,
+            @Nullable TextLinks.Options options) {
+        return addLinksAsync(textView, options, null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text (to avoid
+     * problems if you call it repeatedly on the same text) and sets the movement method for the
+     * TextView to LinkMovementMethod.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * @param textView TextView whose text is to be marked-up with links
+     * @param options optional parameters to specify how to generate the links
+     * @param executor Executor that runs the background task
+     * @param callback Callback that receives the final status of the background task execution
+     *
+     * @return a future that may be used to interrupt or query the background task
+     */
+    @UiThread
+    public static Future<Void> addLinksAsync(
+            @NonNull TextView textView,
+            @Nullable TextLinks.Options options,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback) {
+        Preconditions.checkNotNull(textView);
+        final CharSequence text = textView.getText();
+        final Spannable spannable = (text instanceof Spannable)
+                ? (Spannable) text : SpannableString.valueOf(text);
+        final Runnable modifyTextView = () -> {
+            addLinkMovementMethod(textView);
+            if (spannable != text) {
+                textView.setText(spannable);
+            }
+        };
+        return addLinksAsync(spannable, textView.getTextClassifier(),
+                options, executor, callback, modifyTextView);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param options optional parameters to specify how to generate the links
+     *
+     * @return a future that may be used to interrupt or query the background task
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinks.Options options) {
+        return addLinksAsync(text, classifier, options, null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by the link {@code mask} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param mask mask to define which kinds of links will be generated
+     *
+     * @return a future that may be used to interrupt or query the background task
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @LinkifyMask int mask) {
+        return addLinksAsync(text, classifier, TextLinks.Options.fromLinkMask(mask),
+                null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param options optional parameters to specify how to generate the links
+     * @param executor Executor that runs the background task
+     * @param callback Callback that receives the final status of the background task execution
+     *
+     * @return a future that may be used to interrupt or query the background task
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinks.Options options,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback) {
+        return addLinksAsync(text, classifier, options, executor, callback,
+                null /* modifyTextView */);
+    }
+
+    private static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinks.Options options,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback,
+            @Nullable Runnable modifyTextView) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(classifier);
+        final Supplier<TextLinks> supplier = () -> classifier.generateLinks(text, options);
+        final Consumer<TextLinks> consumer = links -> {
+            if (links.getLinks().isEmpty()) {
+                if (callback != null) {
+                    callback.accept(TextLinks.STATUS_NO_LINKS_FOUND);
+                }
+                return;
+            }
+
+            final TextLinkSpan[] old = text.getSpans(0, text.length(), TextLinkSpan.class);
+            for (int i = old.length - 1; i >= 0; i--) {
+                text.removeSpan(old[i]);
+            }
+
+            final Function<TextLinks.TextLink, TextLinkSpan> spanFactory = (options == null)
+                    ? null : options.getSpanFactory();
+            final @TextLinks.ApplyStrategy int applyStrategy = (options == null)
+                    ? TextLinks.APPLY_STRATEGY_IGNORE : options.getApplyStrategy();
+            final @TextLinks.Status int result =  links.apply(text, applyStrategy, spanFactory);
+            if (result == TextLinks.STATUS_LINKS_APPLIED) {
+                if (modifyTextView != null) {
+                    modifyTextView.run();
+                }
+            }
+            if (callback != null) {
+                callback.accept(result);
+            }
+        };
+        if (executor == null) {
+            return CompletableFuture.supplyAsync(supplier).thenAccept(consumer);
+        } else {
+            return CompletableFuture.supplyAsync(supplier, executor).thenAccept(consumer);
+        }
+    }
+
     private static final void applyLink(String url, int start, int end, Spannable text) {
         URLSpan span = new URLSpan(url);
 
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 8af4b71..410cdc6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -38,12 +38,10 @@
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("device_info_v2", "true");
-        DEFAULT_FLAGS.put("settings_app_info_v2", "true");
         DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
-        DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
     }
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 687aa83..51fb18a 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -60,9 +60,19 @@
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
-        // To prevent breakages of dependencies on old API.
-
-        return false;
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when adding configuration");
+                    return false;
+                }
+                return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+                return false;
+            }
+        }
     }
 
     /**
@@ -99,7 +109,19 @@
     @RequiresPermission(Manifest.permission.DUMP)
     public boolean removeConfiguration(String configKey) {
         // To prevent breakages of old dependencies.
-        return false;
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when removing configuration");
+                    return false;
+                }
+                return service.removeConfiguration(Long.parseLong(configKey));
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+                return false;
+            }
+        }
     }
 
     /**
@@ -132,7 +154,19 @@
     public byte[] getData(String configKey) {
         // TODO: remove this and all other methods with String-based config keys.
         // To prevent build breakages of dependencies.
-        return null;
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when getting data");
+                    return null;
+                }
+                return service.getData(Long.parseLong(configKey));
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connecto statsd when getting data");
+                return null;
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index a4c590f..9436b29 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -438,8 +438,8 @@
         List<Integer> flagsList = new ArrayList<>();
 
         // Proof-of-rotation struct:
-        // is basically a singly linked list of nodes, called levels here, each of which have the
-        // following structure:
+        // A uint32 version code followed by basically a singly linked list of nodes, called levels
+        // here, each of which have the following structure:
         // * length-prefix for the entire level
         //     - length-prefixed signed data (if previous level exists)
         //         * length-prefixed X509 Certificate
@@ -450,9 +450,13 @@
         //     - length-prefixed signature over the signed data in this level.  The signature here
         //         is verified using the certificate from the previous level.
         // The linking is provided by the certificate of each level signing the one of the next.
-        while (porBuf.hasRemaining()) {
-            levelCount++;
-            try {
+
+        try {
+
+            // get the version code, but don't do anything with it: creator knew about all our flags
+            porBuf.getInt();
+            while (porBuf.hasRemaining()) {
+                levelCount++;
                 ByteBuffer level = getLengthPrefixedSlice(porBuf);
                 ByteBuffer signedData = getLengthPrefixedSlice(level);
                 int flags = level.getInt();
@@ -491,17 +495,17 @@
                 lastSigAlgorithm = sigAlgorithm;
                 certs.add(lastCert);
                 flagsList.add(flags);
-            } catch (IOException | BufferUnderflowException e) {
-                throw new IOException("Failed to parse Proof-of-rotation record", e);
-            } catch (NoSuchAlgorithmException | InvalidKeyException
-                    | InvalidAlgorithmParameterException | SignatureException e) {
-                throw new SecurityException(
-                        "Failed to verify signature over signed data for certificate #"
-                                + levelCount + " when verifying Proof-of-rotation record", e);
-            } catch (CertificateException e) {
-                throw new SecurityException("Failed to decode certificate #" + levelCount
-                        + " when verifying Proof-of-rotation record", e);
             }
+        } catch (IOException | BufferUnderflowException e) {
+            throw new IOException("Failed to parse Proof-of-rotation record", e);
+        } catch (NoSuchAlgorithmException | InvalidKeyException
+                | InvalidAlgorithmParameterException | SignatureException e) {
+            throw new SecurityException(
+                    "Failed to verify signature over signed data for certificate #"
+                            + levelCount + " when verifying Proof-of-rotation record", e);
+        } catch (CertificateException e) {
+            throw new SecurityException("Failed to decode certificate #" + levelCount
+                    + " when verifying Proof-of-rotation record", e);
         }
         return new VerifiedProofOfRotation(certs, flagsList);
     }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 5070151..ce7e8f3 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -353,9 +353,24 @@
         return nHasShadow(mNativeRenderNode);
     }
 
-    /** setShadowColor */
-    public boolean setShadowColor(int color) {
-        return nSetShadowColor(mNativeRenderNode, color);
+    /** setSpotShadowColor */
+    public boolean setSpotShadowColor(int color) {
+        return nSetSpotShadowColor(mNativeRenderNode, color);
+    }
+
+    /** setAmbientShadowColor */
+    public boolean setAmbientShadowColor(int color) {
+        return nSetAmbientShadowColor(mNativeRenderNode, color);
+    }
+
+    /** getSpotShadowColor */
+    public int getSpotShadowColor() {
+        return nGetSpotShadowColor(mNativeRenderNode);
+    }
+
+    /** getAmbientShadowColor */
+    public int getAmbientShadowColor() {
+        return nGetAmbientShadowColor(mNativeRenderNode);
     }
 
     /**
@@ -915,7 +930,13 @@
     @CriticalNative
     private static native boolean nHasShadow(long renderNode);
     @CriticalNative
-    private static native boolean nSetShadowColor(long renderNode, int color);
+    private static native boolean nSetSpotShadowColor(long renderNode, int color);
+    @CriticalNative
+    private static native boolean nSetAmbientShadowColor(long renderNode, int color);
+    @CriticalNative
+    private static native int nGetSpotShadowColor(long renderNode);
+    @CriticalNative
+    private static native int nGetAmbientShadowColor(long renderNode);
     @CriticalNative
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
     @CriticalNative
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5150c1f..c3e9e73 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
-
 import static java.lang.Math.max;
 
 import android.animation.AnimatorInflater;
@@ -726,6 +725,8 @@
  * @attr ref android.R.styleable#View_nextFocusRight
  * @attr ref android.R.styleable#View_nextFocusUp
  * @attr ref android.R.styleable#View_onClick
+ * @attr ref android.R.styleable#View_outlineSpotShadowColor
+ * @attr ref android.R.styleable#View_outlineAmbientShadowColor
  * @attr ref android.R.styleable#View_padding
  * @attr ref android.R.styleable#View_paddingHorizontal
  * @attr ref android.R.styleable#View_paddingVertical
@@ -3975,6 +3976,7 @@
     /**
      * Current clip bounds. to which all drawing of this view are constrained.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     Rect mClipBounds = null;
 
     private boolean mLastIsOpaque;
@@ -5446,6 +5448,12 @@
                         setAccessibilityPaneTitle(a.getString(attr));
                     }
                     break;
+                case R.styleable.View_outlineSpotShadowColor:
+                    setOutlineSpotShadowColor(a.getColor(attr, Color.BLACK));
+                    break;
+                case R.styleable.View_outlineAmbientShadowColor:
+                    setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK));
+                    break;
             }
         }
 
@@ -15481,14 +15489,61 @@
     }
 
     /**
-     * @hide
+     * Sets the color of the spot shadow that is drawn when the view has a positive Z or
+     * elevation value.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different views with different colors.
+     * <p>
+     * The opacity of the final spot shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineSpotShadowColor (typically opaque), and the
+     * {@link android.R.attr#spotShadowAlpha} theme attribute.
+     *
+     * @attr ref android.R.styleable#View_outlineSpotShadowColor
+     * @param color The color this View will cast for its elevation spot shadow.
      */
-    public void setShadowColor(@ColorInt int color) {
-        if (mRenderNode.setShadowColor(color)) {
+    public void setOutlineSpotShadowColor(@ColorInt int color) {
+        if (mRenderNode.setSpotShadowColor(color)) {
             invalidateViewProperty(true, true);
         }
     }
 
+    /**
+     * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
+     * was set
+     */
+    public @ColorInt int getOutlineSpotShadowColor() {
+        return mRenderNode.getSpotShadowColor();
+    }
+
+    /**
+     * Sets the color of the ambient shadow that is drawn when the view has a positive Z or
+     * elevation value.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different views with different colors.
+     * <p>
+     * The opacity of the final ambient shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+     * {@link android.R.attr#ambientShadowAlpha} theme attribute.
+     *
+     * @attr ref android.R.styleable#View_outlineAmbientShadowColor
+     * @param color The color this View will cast for its elevation shadow.
+     */
+    public void setOutlineAmbientShadowColor(@ColorInt int color) {
+        if (mRenderNode.setAmbientShadowColor(color)) {
+            invalidateViewProperty(true, true);
+        }
+    }
+
+    /**
+     * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
+     * nothing was set
+     */
+    public @ColorInt int getOutlineAmbientShadowColor() {
+        return mRenderNode.getAmbientShadowColor();
+    }
+
 
     /** @hide */
     public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
@@ -23634,9 +23689,16 @@
         Point shadowTouchPoint = new Point();
         shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
 
-        if ((shadowSize.x <= 0) || (shadowSize.y <= 0)
+        if ((shadowSize.x < 0) || (shadowSize.y < 0)
                 || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
-            throw new IllegalStateException("Drag shadow dimensions must be positive");
+            throw new IllegalStateException("Drag shadow dimensions must not be negative");
+        }
+
+        // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
+        // does not accept zero size surface.
+        if (shadowSize.x == 0  || shadowSize.y == 0) {
+            shadowSize.x = 1;
+            shadowSize.y = 1;
         }
 
         if (ViewDebug.DEBUG_DRAG) {
@@ -26979,6 +27041,8 @@
         stream.addProperty("drawing:scaleY", getScaleY());
         stream.addProperty("drawing:pivotX", getPivotX());
         stream.addProperty("drawing:pivotY", getPivotY());
+        stream.addProperty("drawing:clipBounds",
+                mClipBounds == null ? null : mClipBounds.toString());
         stream.addProperty("drawing:opaque", isOpaque());
         stream.addProperty("drawing:alpha", getAlpha());
         stream.addProperty("drawing:transitionAlpha", getTransitionAlpha());
@@ -26990,6 +27054,8 @@
         stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing());
         stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled());
         stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering());
+        stream.addProperty("drawing:outlineAmbientShadowColor", getOutlineAmbientShadowColor());
+        stream.addProperty("drawing:outlineSpotShadowColor", getOutlineSpotShadowColor());
 
         // focus
         stream.addProperty("focus:hasFocus", hasFocus());
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 4b24a71..dac1998 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1099,6 +1099,30 @@
     }
 
     /**
+     * Gets the id of the {@link UserData} used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>.
+     *
+     * <p>This method is useful when the service must check the status of the {@link UserData} in
+     * the device without fetching the whole object.
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+     * and it's ignored if the caller currently doesn't have an enabled autofill service for
+     * the user.
+     *
+     * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
+     * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
+     * service for the user.
+     */
+    @Nullable public String getUserDataId() {
+        try {
+            return mService.getUserDataId();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
      * Gets the user data used for
      * <a href="AutofillService.html#FieldClassification">field classification</a>.
      *
@@ -1119,7 +1143,7 @@
     }
 
     /**
-     * Sets the user data used for
+     * Sets the {@link UserData} used for
      * <a href="AutofillService.html#FieldClassification">field classification</a>
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 1a11fbb..0018547 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -56,6 +56,7 @@
     boolean isServiceEnabled(int userId, String packageName);
     void onPendingSaveUi(int operation, IBinder token);
     UserData getUserData();
+    String getUserDataId();
     void setUserData(in UserData userData);
     boolean isFieldClassificationEnabled();
     ComponentName getAutofillServiceComponentName();
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
new file mode 100644
index 0000000..af55dcd
--- /dev/null
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLinksCallback;
+import android.service.textclassifier.ITextSelectionCallback;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Proxy to the system's default TextClassifier.
+ */
+final class SystemTextClassifier implements TextClassifier {
+
+    private static final String LOG_TAG = "SystemTextClassifier";
+
+    private final ITextClassifierService mManagerService;
+    private final TextClassifier mFallback;
+
+    SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException {
+        mManagerService = ITextClassifierService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
+        mFallback = new TextClassifierImpl(context);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @WorkerThread
+    public TextSelection suggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex,
+            @Nullable TextSelection.Options options) {
+        Utils.validate(text, selectionStartIndex, selectionEndIndex, false /* allowInMainThread */);
+        try {
+            final TextSelectionCallback callback = new TextSelectionCallback();
+            mManagerService.onSuggestSelection(
+                    text, selectionStartIndex, selectionEndIndex, options, callback);
+            final TextSelection selection = callback.mReceiver.get();
+            if (selection != null) {
+                return selection;
+            }
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        } catch (InterruptedException e) {
+            Log.d(LOG_TAG, e.getMessage());
+        }
+        return mFallback.suggestSelection(text, selectionStartIndex, selectionEndIndex, options);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @WorkerThread
+    public TextClassification classifyText(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int startIndex,
+            @IntRange(from = 0) int endIndex,
+            @Nullable TextClassification.Options options) {
+        Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
+        try {
+            final TextClassificationCallback callback = new TextClassificationCallback();
+            mManagerService.onClassifyText(text, startIndex, endIndex, options, callback);
+            final TextClassification classification = callback.mReceiver.get();
+            if (classification != null) {
+                return classification;
+            }
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        } catch (InterruptedException e) {
+            Log.d(LOG_TAG, e.getMessage());
+        }
+        return mFallback.classifyText(text, startIndex, endIndex, options);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @WorkerThread
+    public TextLinks generateLinks(
+            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
+        Utils.validate(text, false /* allowInMainThread */);
+        try {
+            final TextLinksCallback callback = new TextLinksCallback();
+            mManagerService.onGenerateLinks(text, options, callback);
+            final TextLinks links = callback.mReceiver.get();
+            if (links != null) {
+                return links;
+            }
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        } catch (InterruptedException e) {
+            Log.d(LOG_TAG, e.getMessage());
+        }
+        return mFallback.generateLinks(text, options);
+    }
+
+    private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
+
+        final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextSelection selection) {
+            mReceiver.onSuccess(selection);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class TextClassificationCallback extends ITextClassificationCallback.Stub {
+
+        final ResponseReceiver<TextClassification> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextClassification classification) {
+            mReceiver.onSuccess(classification);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class TextLinksCallback extends ITextLinksCallback.Stub {
+
+        final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextLinks links) {
+            mReceiver.onSuccess(links);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class ResponseReceiver<T> {
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+
+        private T mResponse;
+
+        public void onSuccess(T response) {
+            mResponse = response;
+            mLatch.countDown();
+        }
+
+        public void onFailure() {
+            Log.e(LOG_TAG, "Request failed.", null);
+            mLatch.countDown();
+        }
+
+        @Nullable
+        public T get() throws InterruptedException {
+            // If this is running on the main thread, do not block for a response.
+            // The response will unfortunately be null and the TextClassifier should depend on its
+            // fallback.
+            // NOTE that TextClassifier calls should preferably always be called on a worker thread.
+            if (Looper.myLooper() != Looper.getMainLooper()) {
+                mLatch.await(2, TimeUnit.SECONDS);
+            }
+            return mResponse;
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.aidl b/core/java/android/view/textclassifier/TextClassification.aidl
new file mode 100644
index 0000000..9fefe5d
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassification.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+parcelable TextClassification;
+parcelable TextClassification.Options;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 54e93d5..a6a2a94 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -97,7 +97,7 @@
  *   });
  * }</pre>
  */
-public final class TextClassification {
+public final class TextClassification implements Parcelable {
 
     /**
      * @hide
@@ -310,42 +310,6 @@
                 mSignature);
     }
 
-    /** Helper for parceling via #ParcelableWrapper. */
-    private void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mText);
-        final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
-        dest.writeInt(primaryIconBitmap != null ? 1 : 0);
-        if (primaryIconBitmap != null) {
-            primaryIconBitmap.writeToParcel(dest, flags);
-        }
-        dest.writeString(mPrimaryLabel);
-        dest.writeInt(mPrimaryIntent != null ? 1 : 0);
-        if (mPrimaryIntent != null) {
-            mPrimaryIntent.writeToParcel(dest, flags);
-        }
-        // mPrimaryOnClickListener is not parcelable.
-        dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
-        dest.writeStringList(mSecondaryLabels);
-        dest.writeTypedList(mSecondaryIntents);
-        mEntityConfidence.writeToParcel(dest, flags);
-        dest.writeString(mSignature);
-    }
-
-    /** Helper for unparceling via #ParcelableWrapper. */
-    private TextClassification(Parcel in) {
-        mText = in.readString();
-        mPrimaryIcon = in.readInt() == 0
-                ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
-        mPrimaryLabel = in.readString();
-        mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
-        mPrimaryOnClickListener = null;  // not parcelable
-        mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
-        mSecondaryLabels = in.createStringArrayList();
-        mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
-        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
-        mSignature = in.readString();
-    }
-
     /**
      * Creates an OnClickListener that starts an activity with the specified intent.
      *
@@ -675,46 +639,56 @@
         }
     }
 
-    /**
-     * Parcelable wrapper for TextClassification objects.
-     * @hide
-     */
-    public static final class ParcelableWrapper implements Parcelable {
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 
-        @NonNull private TextClassification mTextClassification;
-
-        public ParcelableWrapper(@NonNull TextClassification textClassification) {
-            Preconditions.checkNotNull(textClassification);
-            mTextClassification = textClassification;
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mText);
+        final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
+        dest.writeInt(primaryIconBitmap != null ? 1 : 0);
+        if (primaryIconBitmap != null) {
+            primaryIconBitmap.writeToParcel(dest, flags);
         }
-
-        @NonNull
-        public TextClassification getTextClassification() {
-            return mTextClassification;
+        dest.writeString(mPrimaryLabel);
+        dest.writeInt(mPrimaryIntent != null ? 1 : 0);
+        if (mPrimaryIntent != null) {
+            mPrimaryIntent.writeToParcel(dest, flags);
         }
+        // mPrimaryOnClickListener is not parcelable.
+        dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
+        dest.writeStringList(mSecondaryLabels);
+        dest.writeTypedList(mSecondaryIntents);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeString(mSignature);
+    }
 
-        @Override
-        public int describeContents() {
-            return 0;
-        }
+    public static final Parcelable.Creator<TextClassification> CREATOR =
+            new Parcelable.Creator<TextClassification>() {
+                @Override
+                public TextClassification createFromParcel(Parcel in) {
+                    return new TextClassification(in);
+                }
 
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            mTextClassification.writeToParcel(dest, flags);
-        }
+                @Override
+                public TextClassification[] newArray(int size) {
+                    return new TextClassification[size];
+                }
+            };
 
-        public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
-                new Parcelable.Creator<ParcelableWrapper>() {
-                    @Override
-                    public ParcelableWrapper createFromParcel(Parcel in) {
-                        return new ParcelableWrapper(new TextClassification(in));
-                    }
-
-                    @Override
-                    public ParcelableWrapper[] newArray(int size) {
-                        return new ParcelableWrapper[size];
-                    }
-                };
-
+    private TextClassification(Parcel in) {
+        mText = in.readString();
+        mPrimaryIcon = in.readInt() == 0
+                ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
+        mPrimaryLabel = in.readString();
+        mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
+        mPrimaryOnClickListener = null;  // not parcelable
+        mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
+        mSecondaryLabels = in.createStringArrayList();
+        mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
+        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+        mSignature = in.readString();
     }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index d7b0776..300aef2 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -19,6 +19,8 @@
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.ServiceManager;
+import android.service.textclassifier.TextClassifierService;
 
 import com.android.internal.util.Preconditions;
 
@@ -28,10 +30,16 @@
 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
 public final class TextClassificationManager {
 
-    private final Object mTextClassifierLock = new Object();
+    // TODO: Make this a configurable flag.
+    private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED = true;
+
+    private static final String LOG_TAG = "TextClassificationManager";
+
+    private final Object mLock = new Object();
 
     private final Context mContext;
     private TextClassifier mTextClassifier;
+    private TextClassifier mSystemTextClassifier;
 
     /** @hide */
     public TextClassificationManager(Context context) {
@@ -39,12 +47,39 @@
     }
 
     /**
+     * Returns the system's default TextClassifier.
+     * @hide
+     */
+    // TODO: Unhide when this is ready.
+    public TextClassifier getSystemDefaultTextClassifier() {
+        synchronized (mLock) {
+            if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+                try {
+                    Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+                    mSystemTextClassifier = new SystemTextClassifier(mContext);
+                } catch (ServiceManager.ServiceNotFoundException e) {
+                    Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
+                }
+            }
+            if (mSystemTextClassifier == null) {
+                Log.d(LOG_TAG, "Using an in-process TextClassifier as the system default");
+                mSystemTextClassifier = new TextClassifierImpl(mContext);
+            }
+        }
+        return mSystemTextClassifier;
+    }
+
+    /**
      * Returns the text classifier.
      */
     public TextClassifier getTextClassifier() {
-        synchronized (mTextClassifierLock) {
+        synchronized (mLock) {
             if (mTextClassifier == null) {
-                mTextClassifier = new TextClassifierImpl(mContext);
+                if (isSystemTextClassifierEnabled()) {
+                    mTextClassifier = getSystemDefaultTextClassifier();
+                } else {
+                    mTextClassifier = new TextClassifierImpl(mContext);
+                }
             }
             return mTextClassifier;
         }
@@ -56,8 +91,13 @@
      * Set to {@link TextClassifier#NO_OP} to disable text classifier features.
      */
     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
-        synchronized (mTextClassifierLock) {
+        synchronized (mLock) {
             mTextClassifier = textClassifier;
         }
     }
+
+    private boolean isSystemTextClassifierEnabled() {
+        return SYSTEM_TEXT_CLASSIFIER_ENABLED
+                && TextClassifierService.getServiceComponentName(mContext) != null;
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 04ab447..9f75c4a 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -23,9 +23,12 @@
 import android.annotation.StringDef;
 import android.annotation.WorkerThread;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
+import android.util.Slog;
+import android.view.textclassifier.logging.Logger;
 
 import com.android.internal.util.Preconditions;
 
@@ -39,8 +42,8 @@
 /**
  * Interface for providing text classification related features.
  *
- * <p>Unless otherwise stated, methods of this interface are blocking operations.
- * Avoid calling them on the UI thread.
+ * <p><strong>NOTE: </strong>Unless otherwise stated, methods of this interface are blocking
+ * operations. Call on a worker thread.
  */
 public interface TextClassifier {
 
@@ -107,6 +110,8 @@
      * Returns suggested text selection start and end indices, recognized entity types, and their
      * associated confidence scores. The entity types are ordered from highest to lowest scoring.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * @param text text providing context for the selected text (which is specified
      *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
      * @param selectionStartIndex start index of the selected part of text
@@ -125,7 +130,7 @@
             @IntRange(from = 0) int selectionStartIndex,
             @IntRange(from = 0) int selectionEndIndex,
             @Nullable TextSelection.Options options) {
-        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
+        Utils.validate(text, selectionStartIndex, selectionEndIndex, false);
         return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
     }
 
@@ -137,6 +142,8 @@
      * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
      * calls this method, a stack overflow error will happen.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * @param text text providing context for the selected text (which is specified
      *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
      * @param selectionStartIndex start index of the selected part of text
@@ -161,6 +168,8 @@
      * See {@link #suggestSelection(CharSequence, int, int)} or
      * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
      * calls this method, a stack overflow error will happen.
@@ -182,6 +191,8 @@
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * @param text text providing context for the text to classify (which is specified
      *      by the sub sequence starting at startIndex and ending at endIndex)
      * @param startIndex start index of the text to classify
@@ -200,7 +211,7 @@
             @IntRange(from = 0) int startIndex,
             @IntRange(from = 0) int endIndex,
             @Nullable TextClassification.Options options) {
-        Utils.validateInput(text, startIndex, endIndex);
+        Utils.validate(text, startIndex, endIndex, false);
         return TextClassification.EMPTY;
     }
 
@@ -208,6 +219,8 @@
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
      * calls this method, a stack overflow error will happen.
@@ -235,6 +248,8 @@
      * See {@link #classifyText(CharSequence, int, int, TextClassification.Options)} or
      * {@link #classifyText(CharSequence, int, int)}.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
      * calls this method, a stack overflow error will happen.
@@ -253,10 +268,10 @@
     }
 
     /**
-     * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
-     * information.
+     * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
+     * links information.
      *
-     * If no options are supplied, default values will be used, determined by the TextClassifier.
+     * <p><strong>NOTE: </strong>Call on a worker thread.
      *
      * @param text the text to generate annotations for
      * @param options configuration for link generation
@@ -268,13 +283,15 @@
     @WorkerThread
     default TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        Utils.validateInput(text);
+        Utils.validate(text, false);
         return new TextLinks.Builder(text.toString()).build();
     }
 
     /**
-     * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
-     * information.
+     * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
+     * links information.
+     *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
      *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #generateLinks(CharSequence, TextLinks.Options)}. If that method calls this method,
@@ -296,20 +313,22 @@
      *
      * @see #ENTITY_PRESET_ALL
      * @see #ENTITY_PRESET_NONE
+     * @see #ENTITY_PRESET_BASE
      */
     default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
         return Collections.EMPTY_LIST;
     }
 
     /**
-     * Logs a TextClassifier event.
+     * Returns a helper for logging TextClassifier related events.
      *
-     * @param source the text classifier used to generate this event
-     * @param event the text classifier related event
-     * @hide
+     * @param config logger configuration
      */
     @WorkerThread
-    default void logEvent(String source, String event) {}
+    default Logger getLogger(@NonNull Logger.Config config) {
+        Preconditions.checkNotNull(config);
+        return Logger.DISABLED;
+    }
 
     /**
      * Returns this TextClassifier's settings.
@@ -424,19 +443,28 @@
          *      endIndex is greater than text.length() or is not greater than startIndex;
          *      options is null
          */
-        static void validateInput(
-                @NonNull CharSequence text, int startIndex, int endIndex) {
+        public static void validate(
+                @NonNull CharSequence text, int startIndex, int endIndex,
+                boolean allowInMainThread) {
             Preconditions.checkArgument(text != null);
             Preconditions.checkArgument(startIndex >= 0);
             Preconditions.checkArgument(endIndex <= text.length());
             Preconditions.checkArgument(endIndex > startIndex);
+            checkMainThread(allowInMainThread);
         }
 
         /**
          * @throws IllegalArgumentException if text is null or options is null
          */
-        static void validateInput(@NonNull CharSequence text) {
+        public static void validate(@NonNull CharSequence text, boolean allowInMainThread) {
             Preconditions.checkArgument(text != null);
+            checkMainThread(allowInMainThread);
+        }
+
+        private static void checkMainThread(boolean allowInMainThread) {
+            if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
+                Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
+            }
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 6a44fb3..9f389ba 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -35,6 +35,8 @@
 import android.provider.Settings;
 import android.text.util.Linkify;
 import android.util.Patterns;
+import android.view.textclassifier.logging.DefaultLogger;
+import android.view.textclassifier.logging.Logger;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -43,6 +45,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -66,13 +69,13 @@
  *
  * @hide
  */
-final class TextClassifierImpl implements TextClassifier {
+public final class TextClassifierImpl implements TextClassifier {
 
     private static final String LOG_TAG = DEFAULT_LOG_TAG;
     private static final String MODEL_DIR = "/etc/textclassifier/";
-    private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model";
+    private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
     private static final String UPDATED_MODEL_FILE_PATH =
-            "/data/misc/textclassifier/textclassifier.smartselection.model";
+            "/data/misc/textclassifier/textclassifier.model";
     private static final List<String> ENTITY_TYPES_ALL =
             Collections.unmodifiableList(Arrays.asList(
                     TextClassifier.TYPE_ADDRESS,
@@ -90,30 +93,39 @@
                     TextClassifier.TYPE_URL));
 
     private final Context mContext;
+    private final TextClassifier mFallback;
 
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
-    private final Object mSmartSelectionLock = new Object();
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+    private final Object mLock = new Object();
+    @GuardedBy("mLock") // Do not access outside this lock.
     private Map<Locale, String> mModelFilePaths;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+    @GuardedBy("mLock") // Do not access outside this lock.
     private Locale mLocale;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+    @GuardedBy("mLock") // Do not access outside this lock.
     private int mVersion;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+    @GuardedBy("mLock") // Do not access outside this lock.
     private SmartSelection mSmartSelection;
 
+    private final Object mLoggerLock = new Object();
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private WeakReference<Logger.Config> mLoggerConfig = new WeakReference<>(null);
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private Logger mLogger;  // Should never be null if mLoggerConfig.get() is not null.
+
     private TextClassifierConstants mSettings;
 
-    TextClassifierImpl(Context context) {
+    public TextClassifierImpl(Context context) {
         mContext = Preconditions.checkNotNull(context);
+        mFallback = TextClassifier.NO_OP;
     }
 
+    /** @inheritDoc */
     @Override
     public TextSelection suggestSelection(
             @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
             @Nullable TextSelection.Options options) {
-        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
+        Utils.validate(text, selectionStartIndex, selectionEndIndex, false /* allowInMainThread */);
         try {
             if (text.length() > 0) {
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
@@ -159,15 +171,16 @@
                     t);
         }
         // Getting here means something went wrong, return a NO_OP result.
-        return TextClassifier.NO_OP.suggestSelection(
+        return mFallback.suggestSelection(
                 text, selectionStartIndex, selectionEndIndex, options);
     }
 
+    /** @inheritDoc */
     @Override
     public TextClassification classifyText(
             @NonNull CharSequence text, int startIndex, int endIndex,
             @Nullable TextClassification.Options options) {
-        Utils.validateInput(text, startIndex, endIndex);
+        Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
         try {
             if (text.length() > 0) {
                 final String string = text.toString();
@@ -186,13 +199,14 @@
             Log.e(LOG_TAG, "Error getting text classification info.", t);
         }
         // Getting here means something went wrong, return a NO_OP result.
-        return TextClassifier.NO_OP.classifyText(text, startIndex, endIndex, options);
+        return mFallback.classifyText(text, startIndex, endIndex, options);
     }
 
+    /** @inheritDoc */
     @Override
     public TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        Utils.validateInput(text);
+        Utils.validate(text, false /* allowInMainThread */);
         final String textString = text.toString();
         final TextLinks.Builder builder = new TextLinks.Builder(textString);
 
@@ -216,14 +230,14 @@
                 for (int i = 0; i < results.length; i++) {
                     entityScores.put(results[i].mCollection, results[i].mScore);
                 }
-                builder.addLink(new TextLinks.TextLink(
-                        textString, span.getStartIndex(), span.getEndIndex(), entityScores));
+                builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores);
             }
+            return builder.build();
         } catch (Throwable t) {
             // Avoid throwing from this method. Log the error.
             Log.e(LOG_TAG, "Error getting links info.", t);
         }
-        return builder.build();
+        return mFallback.generateLinks(text, options);
     }
 
     @Override
@@ -241,12 +255,18 @@
     }
 
     @Override
-    public void logEvent(String source, String event) {
-        if (LOG_TAG.equals(source)) {
-            mMetricsLogger.count(event, 1);
+    public Logger getLogger(@NonNull Logger.Config config) {
+        Preconditions.checkNotNull(config);
+        synchronized (mLoggerLock) {
+            if (mLoggerConfig.get() == null || !mLoggerConfig.get().equals(config)) {
+                mLoggerConfig = new WeakReference<>(config);
+                mLogger = new DefaultLogger(config);
+            }
+            return mLogger;
         }
     }
 
+    /** @hide */
     @Override
     public TextClassifierConstants getSettings() {
         if (mSettings == null) {
@@ -257,7 +277,7 @@
     }
 
     private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
-        synchronized (mSmartSelectionLock) {
+        synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
             final Locale locale = findBestSupportedLocaleLocked(localeList);
             if (locale == null) {
@@ -277,16 +297,12 @@
     }
 
     private String getSignature(String text, int start, int end) {
-        synchronized (mSmartSelectionLock) {
-            final String versionInfo = (mLocale != null)
-                    ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion)
-                    : "";
-            final int hash = Objects.hash(text, start, end, mContext.getPackageName());
-            return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash);
+        synchronized (mLock) {
+            return DefaultLogger.createSignature(text, start, end, mContext, mVersion, mLocale);
         }
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+    @GuardedBy("mLock") // Do not call outside this lock.
     private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
         ParcelFileDescriptor updateFd;
         int updateVersion = -1;
@@ -321,7 +337,7 @@
                 return factoryFd;
             } else {
                 throw new FileNotFoundException(
-                        String.format("No model file found for %s", locale));
+                        String.format(Locale.US, "No model file found for %s", locale));
             }
         }
 
@@ -335,7 +351,7 @@
             } else {
                 closeAndLogError(updateFd);
                 throw new FileNotFoundException(
-                        String.format("No model file found for %s", locale));
+                        String.format(Locale.US, "No model file found for %s", locale));
             }
         }
 
@@ -353,7 +369,7 @@
         }
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+    @GuardedBy("mLock") // Do not call outside this lock.
     private void destroySmartSelectionIfExistsLocked() {
         if (mSmartSelection != null) {
             mSmartSelection.close();
@@ -361,7 +377,7 @@
         }
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+    @GuardedBy("mLock") // Do not call outside this lock.
     @Nullable
     private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
         // Specified localeList takes priority over the system default, so it is listed first.
@@ -379,7 +395,7 @@
         return Locale.lookup(languageRangeList, supportedLocales);
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+    @GuardedBy("mLock") // Do not call outside this lock.
     private Map<Locale, String> getFactoryModelFilePathsLocked() {
         if (mModelFilePaths == null) {
             final Map<Locale, String> modelFilePaths = new HashMap<>();
diff --git a/core/java/android/view/textclassifier/TextLinks.aidl b/core/java/android/view/textclassifier/TextLinks.aidl
new file mode 100644
index 0000000..1bbb798
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLinks.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+parcelable TextLinks;
+parcelable TextLinks.Options;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index ba854e0..ede5211 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -17,18 +17,24 @@
 package android.view.textclassifier;
 
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.SpannableString;
+import android.text.Spannable;
 import android.text.style.ClickableSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
 import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -41,12 +47,51 @@
  * address, url, etc) they may be.
  */
 public final class TextLinks implements Parcelable {
+
+    /**
+     * Return status of an attempt to apply TextLinks to text.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
+            STATUS_DIFFERENT_TEXT})
+    public @interface Status {}
+
+    /** Links were successfully applied to the text. */
+    public static final int STATUS_LINKS_APPLIED = 0;
+
+    /** No links exist to apply to text. Links count is zero. */
+    public static final int STATUS_NO_LINKS_FOUND = 1;
+
+    /** No links applied to text. The links were filtered out. */
+    public static final int STATUS_NO_LINKS_APPLIED = 2;
+
+    /** The specified text does not match the text used to generate the links. */
+    public static final int STATUS_DIFFERENT_TEXT = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
+    public @interface ApplyStrategy {}
+
+    /**
+      * Do not replace {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to
+      * be applied to. Do not apply the TextLinkSpan.
+      */
+    public static final int APPLY_STRATEGY_IGNORE = 0;
+
+    /**
+      * Replace any {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to be
+      * applied to.
+      */
+    public static final int APPLY_STRATEGY_REPLACE = 1;
+
     private final String mFullText;
     private final List<TextLink> mLinks;
 
-    private TextLinks(String fullText, Collection<TextLink> links) {
+    private TextLinks(String fullText, ArrayList<TextLink> links) {
         mFullText = fullText;
-        mLinks = Collections.unmodifiableList(new ArrayList<>(links));
+        mLinks = Collections.unmodifiableList(links);
     }
 
     /**
@@ -60,29 +105,57 @@
      * Annotates the given text with the generated links. It will fail if the provided text doesn't
      * match the original text used to crete the TextLinks.
      *
-     * @param text the text to apply the links to. Must match the original text.
-     * @param spanFactory a factory to generate spans from TextLinks. Will use a default if null.
+     * @param text the text to apply the links to. Must match the original text
+     * @param applyStrategy strategy for resolving link conflicts
+     * @param spanFactory a factory to generate spans from TextLinks. Will use a default if null
      *
-     * @return Success or failure.
+     * @return a status code indicating whether or not the links were successfully applied
+     *
+     * @hide
      */
-    public boolean apply(
-            @NonNull SpannableString text,
-            @Nullable Function<TextLink, ClickableSpan> spanFactory) {
+    @Status
+    public int apply(
+            @NonNull Spannable text,
+            @ApplyStrategy int applyStrategy,
+            @Nullable Function<TextLink, TextLinkSpan> spanFactory) {
         Preconditions.checkNotNull(text);
+        checkValidApplyStrategy(applyStrategy);
         if (!mFullText.equals(text.toString())) {
-            return false;
+            return STATUS_DIFFERENT_TEXT;
+        }
+        if (mLinks.isEmpty()) {
+            return STATUS_NO_LINKS_FOUND;
         }
 
         if (spanFactory == null) {
             spanFactory = DEFAULT_SPAN_FACTORY;
         }
+        int applyCount = 0;
         for (TextLink link : mLinks) {
-            final ClickableSpan span = spanFactory.apply(link);
+            final TextLinkSpan span = spanFactory.apply(link);
             if (span != null) {
-                text.setSpan(span, link.getStart(), link.getEnd(), 0);
+                final ClickableSpan[] existingSpans = text.getSpans(
+                        link.getStart(), link.getEnd(), ClickableSpan.class);
+                if (existingSpans.length > 0) {
+                    if (applyStrategy == APPLY_STRATEGY_REPLACE) {
+                        for (ClickableSpan existingSpan : existingSpans) {
+                            text.removeSpan(existingSpan);
+                        }
+                        text.setSpan(span, link.getStart(), link.getEnd(),
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                        applyCount++;
+                    }
+                } else {
+                    text.setSpan(span, link.getStart(), link.getEnd(),
+                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    applyCount++;
+                }
             }
         }
-        return true;
+        if (applyCount == 0) {
+            return STATUS_NO_LINKS_APPLIED;
+        }
+        return STATUS_LINKS_APPLIED;
     }
 
     @Override
@@ -119,7 +192,6 @@
      */
     public static final class TextLink implements Parcelable {
         private final EntityConfidence mEntityScores;
-        private final String mOriginalText;
         private final int mStart;
         private final int mEnd;
 
@@ -128,12 +200,10 @@
          *
          * @throws IllegalArgumentException if entityScores is null or empty.
          */
-        public TextLink(String originalText, int start, int end, Map<String, Float> entityScores) {
-            Preconditions.checkNotNull(originalText);
+        TextLink(int start, int end, Map<String, Float> entityScores) {
             Preconditions.checkNotNull(entityScores);
             Preconditions.checkArgument(!entityScores.isEmpty());
             Preconditions.checkArgument(start <= end);
-            mOriginalText = originalText;
             mStart = start;
             mEnd = end;
             mEntityScores = new EntityConfidence(entityScores);
@@ -171,7 +241,7 @@
          *
          * @return the entity type at the provided index.
          */
-        @NonNull public @TextClassifier.EntityType String getEntity(int index) {
+        @NonNull public @EntityType String getEntity(int index) {
             return mEntityScores.getEntities().get(index);
         }
 
@@ -181,7 +251,7 @@
          * @param entityType the entity type.
          */
         public @FloatRange(from = 0.0, to = 1.0) float getConfidenceScore(
-                @TextClassifier.EntityType String entityType) {
+                @EntityType String entityType) {
             return mEntityScores.getConfidenceScore(entityType);
         }
 
@@ -193,7 +263,6 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             mEntityScores.writeToParcel(dest, flags);
-            dest.writeString(mOriginalText);
             dest.writeInt(mStart);
             dest.writeInt(mEnd);
         }
@@ -213,7 +282,6 @@
 
         private TextLink(Parcel in) {
             mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
-            mOriginalText = in.readString();
             mStart = in.readInt();
             mEnd = in.readInt();
         }
@@ -227,6 +295,32 @@
         private LocaleList mDefaultLocales;
         private TextClassifier.EntityConfig mEntityConfig;
 
+        private @ApplyStrategy int mApplyStrategy;
+        private Function<TextLink, TextLinkSpan> mSpanFactory;
+
+        /**
+         * Returns a new options object based on the specified link mask.
+         */
+        public static Options fromLinkMask(@LinkifyMask int mask) {
+            final TextClassifier.EntityConfig entityConfig =
+                    new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE);
+
+            if ((mask & Linkify.WEB_URLS) != 0) {
+                entityConfig.includeEntities(TextClassifier.TYPE_URL);
+            }
+            if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
+                entityConfig.includeEntities(TextClassifier.TYPE_EMAIL);
+            }
+            if ((mask & Linkify.PHONE_NUMBERS) != 0) {
+                entityConfig.includeEntities(TextClassifier.TYPE_PHONE);
+            }
+            if ((mask & Linkify.MAP_ADDRESSES) != 0) {
+                entityConfig.includeEntities(TextClassifier.TYPE_ADDRESS);
+            }
+
+            return new Options().setEntityConfig(entityConfig);
+        }
+
         public Options() {}
 
         /**
@@ -251,6 +345,31 @@
         }
 
         /**
+         * Sets a strategy for resolving conflicts when applying generated links to text that
+         * already have links.
+         *
+         * @throws IllegalArgumentException if applyStrategy is not valid
+         *
+         * @see #APPLY_STRATEGY_IGNORE
+         * @see #APPLY_STRATEGY_REPLACE
+         */
+        public Options setApplyStrategy(@ApplyStrategy int applyStrategy) {
+            checkValidApplyStrategy(applyStrategy);
+            mApplyStrategy = applyStrategy;
+            return this;
+        }
+
+        /**
+         * Sets a factory for converting a TextLink to a TextLinkSpan.
+         *
+         * <p><strong>Note: </strong>This is not parceled over IPC.
+         */
+        public Options setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) {
+            mSpanFactory = spanFactory;
+            return this;
+        }
+
+        /**
          * @return ordered list of locale preferences that can be used to disambiguate
          *      the provided text.
          */
@@ -260,7 +379,7 @@
         }
 
         /**
-         * @return The config representing the set of entities to look for.
+         * @return The config representing the set of entities to look for
          * @see #setEntityConfig(TextClassifier.EntityConfig)
          */
         @Nullable
@@ -268,6 +387,29 @@
             return mEntityConfig;
         }
 
+        /**
+         * @return the strategy for resolving conflictswhen applying generated links to text that
+         * already have links.
+         *
+         * @see #APPLY_STRATEGY_IGNORE
+         * @see #APPLY_STRATEGY_REPLACE
+         */
+        @ApplyStrategy
+        public int getApplyStrategy() {
+            return mApplyStrategy;
+        }
+
+        /**
+         * Returns a factory for converting a TextLink to a TextLinkSpan.
+         *
+         * <p><strong>Note: </strong>This is not parcelable and will always return null if read
+         *      from a parcel
+         */
+        @Nullable
+        public Function<TextLink, TextLinkSpan> getSpanFactory() {
+            return mSpanFactory;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -283,6 +425,7 @@
             if (mEntityConfig != null) {
                 mEntityConfig.writeToParcel(dest, flags);
             }
+            dest.writeInt(mApplyStrategy);
         }
 
         public static final Parcelable.Creator<Options> CREATOR =
@@ -305,39 +448,53 @@
             if (in.readInt() > 0) {
                 mEntityConfig = TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
             }
+            mApplyStrategy = in.readInt();
         }
     }
 
     /**
      * A function to create spans from TextLinks.
-     *
-     * Applies only to TextViews.
-     * We can hide this until we are convinced we want it to be part of the public API.
-     *
-     * @hide
      */
-    public static final Function<TextLink, ClickableSpan> DEFAULT_SPAN_FACTORY =
-            textLink -> new ClickableSpan() {
-                @Override
-                public void onClick(View widget) {
-                    if (widget instanceof TextView) {
-                        final TextView textView = (TextView) widget;
-                        textView.requestActionMode(textLink);
-                    }
-                }
-            };
+    private static final Function<TextLink, TextLinkSpan> DEFAULT_SPAN_FACTORY =
+            textLink -> new TextLinkSpan(textLink);
+
+    /**
+     * A ClickableSpan for a TextLink.
+     *
+     * <p>Applies only to TextViews.
+     */
+    public static class TextLinkSpan extends ClickableSpan {
+
+        private final TextLink mTextLink;
+
+        public TextLinkSpan(@Nullable TextLink textLink) {
+            mTextLink = textLink;
+        }
+
+        @Override
+        public void onClick(View widget) {
+            if (widget instanceof TextView) {
+                final TextView textView = (TextView) widget;
+                textView.requestActionMode(mTextLink);
+            }
+        }
+
+        public final TextLink getTextLink() {
+            return mTextLink;
+        }
+    }
 
     /**
      * A builder to construct a TextLinks instance.
      */
     public static final class Builder {
         private final String mFullText;
-        private final Collection<TextLink> mLinks;
+        private final ArrayList<TextLink> mLinks;
 
         /**
          * Create a new TextLinks.Builder.
          *
-         * @param fullText The full text that links will be added to.
+         * @param fullText The full text to annotate with links.
          */
         public Builder(@NonNull String fullText) {
             mFullText = Preconditions.checkNotNull(fullText);
@@ -348,10 +505,19 @@
          * Adds a TextLink.
          *
          * @return this instance.
+         *
+         * @throws IllegalArgumentException if entityScores is null or empty.
          */
-        public Builder addLink(TextLink link) {
-            Preconditions.checkNotNull(link);
-            mLinks.add(link);
+        public Builder addLink(int start, int end, Map<String, Float> entityScores) {
+            mLinks.add(new TextLink(start, end, entityScores));
+            return this;
+        }
+
+        /**
+         * Removes all {@link TextLink}s.
+         */
+        public Builder clearTextLinks() {
+            mLinks.clear();
             return this;
         }
 
@@ -364,4 +530,14 @@
             return new TextLinks(mFullText, mLinks);
         }
     }
+
+    /**
+     * @throws IllegalArgumentException if the value is invalid
+     */
+    private static void checkValidApplyStrategy(int applyStrategy) {
+        if (applyStrategy != APPLY_STRATEGY_IGNORE && applyStrategy != APPLY_STRATEGY_REPLACE) {
+            throw new IllegalArgumentException(
+                    "Invalid apply strategy. See TextLinks.ApplyStrategy for options.");
+        }
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextSelection.aidl b/core/java/android/view/textclassifier/TextSelection.aidl
new file mode 100644
index 0000000..dab1aef
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextSelection.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+parcelable TextSelection;
+parcelable TextSelection.Options;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 774d42d..1c93be7 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -34,7 +34,7 @@
 /**
  * Information about where text selection should be.
  */
-public final class TextSelection {
+public final class TextSelection implements Parcelable {
 
     private final int mStartIndex;
     private final int mEndIndex;
@@ -112,22 +112,6 @@
                 mStartIndex, mEndIndex, mEntityConfidence, mSignature);
     }
 
-    /** Helper for parceling via #ParcelableWrapper. */
-    private void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mStartIndex);
-        dest.writeInt(mEndIndex);
-        mEntityConfidence.writeToParcel(dest, flags);
-        dest.writeString(mSignature);
-    }
-
-    /** Helper for unparceling via #ParcelableWrapper. */
-    private TextSelection(Parcel in) {
-        mStartIndex = in.readInt();
-        mEndIndex = in.readInt();
-        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
-        mSignature = in.readString();
-    }
-
     /**
      * Builder used to build {@link TextSelection} objects.
      */
@@ -272,46 +256,36 @@
         }
     }
 
-    /**
-     * Parcelable wrapper for TextSelection objects.
-     * @hide
-     */
-    public static final class ParcelableWrapper implements Parcelable {
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 
-        @NonNull private TextSelection mTextSelection;
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mStartIndex);
+        dest.writeInt(mEndIndex);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeString(mSignature);
+    }
 
-        public ParcelableWrapper(@NonNull TextSelection textSelection) {
-            Preconditions.checkNotNull(textSelection);
-            mTextSelection = textSelection;
-        }
+    public static final Parcelable.Creator<TextSelection> CREATOR =
+            new Parcelable.Creator<TextSelection>() {
+                @Override
+                public TextSelection createFromParcel(Parcel in) {
+                    return new TextSelection(in);
+                }
 
-        @NonNull
-        public TextSelection getTextSelection() {
-            return mTextSelection;
-        }
+                @Override
+                public TextSelection[] newArray(int size) {
+                    return new TextSelection[size];
+                }
+            };
 
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            mTextSelection.writeToParcel(dest, flags);
-        }
-
-        public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
-                new Parcelable.Creator<ParcelableWrapper>() {
-                    @Override
-                    public ParcelableWrapper createFromParcel(Parcel in) {
-                        return new ParcelableWrapper(new TextSelection(in));
-                    }
-
-                    @Override
-                    public ParcelableWrapper[] newArray(int size) {
-                        return new ParcelableWrapper[size];
-                    }
-                };
-
+    private TextSelection(Parcel in) {
+        mStartIndex = in.readInt();
+        mEndIndex = in.readInt();
+        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+        mSignature = in.readString();
     }
 }
diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java
new file mode 100644
index 0000000..6b84835
--- /dev/null
+++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.logging;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.metrics.LogMaker;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Default Logger.
+ * Used internally by TextClassifierImpl.
+ * @hide
+ */
+public final class DefaultLogger extends Logger {
+
+    private static final String LOG_TAG = "DefaultLogger";
+    private static final String CLASSIFIER_ID = "androidtc";
+
+    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
+    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
+    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
+    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+    private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
+    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
+    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
+    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
+    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
+    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
+
+    private static final String ZERO = "0";
+    private static final String UNKNOWN = "unknown";
+
+    private final MetricsLogger mMetricsLogger;
+
+    public DefaultLogger(@NonNull Config config) {
+        super(config);
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    @VisibleForTesting
+    public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) {
+        super(config);
+        mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
+    }
+
+    @Override
+    public boolean isSmartSelection(@NonNull String signature) {
+        return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+    }
+
+    @Override
+    public void writeEvent(@NonNull SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
+                .setType(getLogType(event))
+                .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
+                .setPackageName(event.getPackageName())
+                .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
+                .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
+                .addTaggedData(INDEX, event.getEventIndex())
+                .addTaggedData(WIDGET_TYPE, event.getWidgetType())
+                .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
+                .addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getSignature()))
+                .addTaggedData(ENTITY_TYPE, event.getEntityType())
+                .addTaggedData(SMART_START, event.getSmartStart())
+                .addTaggedData(SMART_END, event.getSmartEnd())
+                .addTaggedData(EVENT_START, event.getStart())
+                .addTaggedData(EVENT_END, event.getEnd())
+                .addTaggedData(SESSION_ID, event.getSessionId());
+        mMetricsLogger.write(log);
+        debugLog(log);
+    }
+
+    private static int getLogType(SelectionEvent event) {
+        switch (event.getEventType()) {
+            case SelectionEvent.ACTION_OVERTYPE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
+            case SelectionEvent.ACTION_COPY:
+                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
+            case SelectionEvent.ACTION_PASTE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
+            case SelectionEvent.ACTION_CUT:
+                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
+            case SelectionEvent.ACTION_SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
+            case SelectionEvent.ACTION_SMART_SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+            case SelectionEvent.ACTION_DRAG:
+                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
+            case SelectionEvent.ACTION_ABANDON:
+                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
+            case SelectionEvent.ACTION_OTHER:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
+            case SelectionEvent.ACTION_SELECT_ALL:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
+            case SelectionEvent.ACTION_RESET:
+                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
+            case SelectionEvent.EVENT_SELECTION_STARTED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_START;
+            case SelectionEvent.EVENT_SELECTION_MODIFIED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
+            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
+            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
+            case SelectionEvent.EVENT_AUTO_SELECTION:
+                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
+            default:
+                return MetricsEvent.VIEW_UNKNOWN;
+        }
+    }
+
+    private static String getLogTypeString(int logType) {
+        switch (logType) {
+            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
+                return "OVERTYPE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
+                return "COPY";
+            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
+                return "PASTE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
+                return "CUT";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
+                return "SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
+                return "SMART_SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
+                return "DRAG";
+            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
+                return "ABANDON";
+            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
+                return "OTHER";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
+                return "SELECT_ALL";
+            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
+                return "RESET";
+            case MetricsEvent.ACTION_TEXT_SELECTION_START:
+                return "SELECTION_STARTED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
+                return "SELECTION_MODIFIED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
+                return "SMART_SELECTION_SINGLE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
+                return "SMART_SELECTION_MULTI";
+            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
+                return "AUTO_SELECTION";
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    private static void debugLog(LogMaker log) {
+        if (!DEBUG_LOG_ENABLED) return;
+
+        final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
+        final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
+        final String widget = widgetVersion.isEmpty()
+                ? widgetType : widgetType + "-" + widgetVersion;
+        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
+        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
+            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
+            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
+            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
+        }
+
+        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
+        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
+        final String type = getLogTypeString(log.getType());
+        final int smartStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_START), ZERO));
+        final int smartEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_END), ZERO));
+        final int eventStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
+        final int eventEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_END), ZERO));
+
+        Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
+    }
+
+    /**
+     * Creates a signature string that may be used to tag TextClassifier results.
+     */
+    public static String createSignature(
+            String text, int start, int end, Context context, int modelVersion,
+            @Nullable Locale locale) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(context);
+        final String modelName = (locale != null)
+                ? String.format(Locale.US, "%s_v%d", locale.toLanguageTag(), modelVersion)
+                : "";
+        final int hash = Objects.hash(text, start, end, context.getPackageName());
+        return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
+    }
+
+    /**
+     * Helper for creating and parsing signature strings for
+     * {@link android.view.textclassifier.TextClassifierImpl}.
+     */
+    @VisibleForTesting
+    public static final class SignatureParser {
+
+        static String createSignature(String classifierId, String modelName, int hash) {
+            return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
+        }
+
+        static String getClassifierId(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int end = signature.indexOf("|");
+            if (end >= 0) {
+                return signature.substring(0, end);
+            }
+            return "";
+        }
+
+        static String getModelName(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int start = signature.indexOf("|");
+            final int end = signature.indexOf("|", start);
+            if (start >= 0 && end >= start) {
+                return signature.substring(start, end);
+            }
+            return "";
+        }
+
+        static int getHash(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int index1 = signature.indexOf("|");
+            final int index2 = signature.indexOf("|", index1);
+            if (index2 > 0) {
+                return Integer.parseInt(signature.substring(index2));
+            }
+            return 0;
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/logging/Logger.java b/core/java/android/view/textclassifier/logging/Logger.java
new file mode 100644
index 0000000..40e4d8c
--- /dev/null
+++ b/core/java/android/view/textclassifier/logging/Logger.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.logging;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.util.Log;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextSelection;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.BreakIterator;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * A helper for logging TextClassifier related events.
+ */
+public abstract class Logger {
+
+    /**
+     * Use this to specify an indeterminate positive index.
+     */
+    public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
+
+    /**
+     * Use this to specify an indeterminate negative index.
+     */
+    public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
+
+    private static final String LOG_TAG = "Logger";
+    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
+
+    private static final String NO_SIGNATURE = "";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({WIDGET_TEXTVIEW, WIDGET_WEBVIEW, WIDGET_EDITTEXT,
+            WIDGET_EDIT_WEBVIEW, WIDGET_CUSTOM_TEXTVIEW, WIDGET_CUSTOM_EDITTEXT,
+            WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW, WIDGET_UNKNOWN})
+    public @interface WidgetType {}
+
+    public static final String WIDGET_TEXTVIEW = "textview";
+    public static final String WIDGET_EDITTEXT = "edittext";
+    public static final String WIDGET_UNSELECTABLE_TEXTVIEW = "nosel-textview";
+    public static final String WIDGET_WEBVIEW = "webview";
+    public static final String WIDGET_EDIT_WEBVIEW = "edit-webview";
+    public static final String WIDGET_CUSTOM_TEXTVIEW = "customview";
+    public static final String WIDGET_CUSTOM_EDITTEXT = "customedit";
+    public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
+    public static final String WIDGET_UNKNOWN = "unknown";
+
+    private SelectionEvent mPrevEvent;
+    private SelectionEvent mSmartEvent;
+    private SelectionEvent mStartEvent;
+
+    /**
+     * Logger that does not log anything.
+     * @hide
+     */
+    public static final Logger DISABLED = new Logger() {
+        @Override
+        public void writeEvent(SelectionEvent event) {}
+    };
+
+    @Nullable
+    private final Config mConfig;
+
+    public Logger(Config config) {
+        mConfig = Preconditions.checkNotNull(config);
+    }
+
+    private Logger() {
+        mConfig = null;
+    }
+
+    /**
+     * Writes the selection event.
+     *
+     * <p><strong>NOTE: </strong>This method is designed for subclasses.
+     * Apps should not call it directly.
+     */
+    public abstract void writeEvent(@NonNull SelectionEvent event);
+
+    /**
+     * Returns true if the signature matches that of a smart selection event (i.e.
+     * {@link SelectionEvent#EVENT_SMART_SELECTION_SINGLE} or
+     * {@link SelectionEvent#EVENT_SMART_SELECTION_MULTI}).
+     * Returns false otherwise.
+     */
+    public boolean isSmartSelection(@NonNull String signature) {
+        return false;
+    }
+
+
+    /**
+     * Returns a token iterator for tokenizing text for logging purposes.
+     */
+    public BreakIterator getTokenIterator(@NonNull Locale locale) {
+        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
+    }
+
+    /**
+     * Logs a "selection started" event.
+     *
+     * @param start  the token index of the selected token
+     */
+    public final void logSelectionStartedEvent(int start) {
+        if (mConfig == null) {
+            return;
+        }
+
+        logEvent(new SelectionEvent(
+                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
+                TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when the user modifies the selection.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     */
+    public final void logSelectionModifiedEvent(int start, int end) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+
+        if (mConfig == null) {
+            return;
+        }
+
+        logEvent(new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when the user modifies the selection and the selection's entity type is known.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     */
+    public final void logSelectionModifiedEvent(
+            int start, int end, @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        final String signature = classification.getSignature();
+        logEvent(new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                entityType, signature, mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when a TextClassifier modifies the selection.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param selection  the TextSelection object returned by the TextClassifier for the
+     *      specified selection
+     */
+    public final void logSelectionModifiedEvent(
+            int start, int end, @NonNull TextSelection selection) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(selection);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final int eventType;
+        if (isSmartSelection(selection.getSignature())) {
+            eventType = end - start > 1
+                    ? SelectionEvent.EVENT_SMART_SELECTION_MULTI
+                    : SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
+
+        } else {
+            eventType = SelectionEvent.EVENT_AUTO_SELECTION;
+        }
+        final String entityType = selection.getEntityCount() > 0
+                ? selection.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        final String signature = selection.getSignature();
+        logEvent(new SelectionEvent(start, end, eventType, entityType, signature, mConfig));
+    }
+
+    /**
+     * Logs an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     */
+    public final void logSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        checkActionType(actionType);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        logEvent(new SelectionEvent(
+                start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+    }
+
+    /**
+     * Logs an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text and the selection's
+     * entity type is known.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
+     */
+    public final void logSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType,
+            @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        checkActionType(actionType);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        final String signature = classification.getSignature();
+        logEvent(new SelectionEvent(start, end, actionType, entityType, signature, mConfig));
+    }
+
+    private void logEvent(@NonNull SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+
+        if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
+                && mStartEvent == null) {
+            if (DEBUG_LOG_ENABLED) {
+                Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
+            }
+            return;
+        }
+
+        final long now = System.currentTimeMillis();
+        switch (event.getEventType()) {
+            case SelectionEvent.EVENT_SELECTION_STARTED:
+                Preconditions.checkArgument(event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
+                event.setSessionId(startNewSession());
+                mStartEvent = event;
+                break;
+            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                mSmartEvent = event;
+                break;
+            case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
+            case SelectionEvent.EVENT_AUTO_SELECTION:
+                if (mPrevEvent != null
+                        && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
+                        && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
+                    // Selection did not change. Ignore event.
+                    return;
+                }
+        }
+
+        event.setEventTime(now);
+        if (mStartEvent != null) {
+            event.setSessionId(mStartEvent.getSessionId())
+                    .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
+                    .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                    .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+        }
+        if (mSmartEvent != null) {
+            event.setSignature(mSmartEvent.getSignature())
+                    .setSmartStart(mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                    .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+        }
+        if (mPrevEvent != null) {
+            event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
+                    .setEventIndex(mPrevEvent.getEventIndex() + 1);
+        }
+        writeEvent(event);
+        mPrevEvent = event;
+
+        if (event.isTerminal()) {
+            endSession();
+        }
+    }
+
+    private String startNewSession() {
+        endSession();
+        return UUID.randomUUID().toString();
+    }
+
+    private void endSession() {
+        mPrevEvent = null;
+        mSmartEvent = null;
+        mStartEvent = null;
+    }
+
+    /**
+     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
+     */
+    private static void checkActionType(@SelectionEvent.EventType int eventType)
+            throws IllegalArgumentException {
+        switch (eventType) {
+            case SelectionEvent.ACTION_OVERTYPE:  // fall through
+            case SelectionEvent.ACTION_COPY:  // fall through
+            case SelectionEvent.ACTION_PASTE:  // fall through
+            case SelectionEvent.ACTION_CUT:  // fall through
+            case SelectionEvent.ACTION_SHARE:  // fall through
+            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
+            case SelectionEvent.ACTION_DRAG:  // fall through
+            case SelectionEvent.ACTION_ABANDON:  // fall through
+            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
+            case SelectionEvent.ACTION_RESET:  // fall through
+                return;
+            default:
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%d is not an eventType", eventType));
+        }
+    }
+
+
+    /**
+     * A Logger config.
+     */
+    public static final class Config {
+
+        private final String mPackageName;
+        private final String mWidgetType;
+        @Nullable private final String mWidgetVersion;
+
+        /**
+         * @param context Context of the widget the logger logs for
+         * @param widgetType a name for the widget being logged for. e.g.
+         *      {@link #WIDGET_TEXTVIEW}
+         * @param widgetVersion a string version info for the widget the logger logs for
+         */
+        public Config(
+                @NonNull Context context,
+                @WidgetType String widgetType,
+                @Nullable String widgetVersion) {
+            mPackageName = Preconditions.checkNotNull(context).getPackageName();
+            mWidgetType = widgetType;
+            mWidgetVersion = widgetVersion;
+        }
+
+        /**
+         * Returns the package name of the application the logger logs for.
+         */
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * Returns the name for the widget being logged for. e.g. {@link #WIDGET_TEXTVIEW}.
+         */
+        public String getWidgetType() {
+            return mWidgetType;
+        }
+
+        /**
+         * Returns string version info for the logger. This is specific to the text classifier.
+         */
+        @Nullable
+        public String getWidgetVersion() {
+            return mWidgetVersion;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mWidgetType, mWidgetVersion);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+
+            if (!(obj instanceof Config)) {
+                return false;
+            }
+
+            final Config other = (Config) obj;
+            return Objects.equals(mPackageName, other.mPackageName)
+                    && Objects.equals(mWidgetType, other.mWidgetType)
+                    && Objects.equals(mWidgetVersion, other.mWidgetType);
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/logging/SelectionEvent.java b/core/java/android/view/textclassifier/logging/SelectionEvent.java
new file mode 100644
index 0000000..f40b655
--- /dev/null
+++ b/core/java/android/view/textclassifier/logging/SelectionEvent.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.logging;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.view.textclassifier.TextClassifier.EntityType;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+
+/**
+ * A selection event.
+ * Specify index parameters as word token indices.
+ */
+public final class SelectionEvent {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
+            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
+            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET})
+    // NOTE: ActionType values should not be lower than 100 to avoid colliding with the other
+    // EventTypes declared below.
+    public @interface ActionType {
+        /*
+         * Terminal event types range: [100,200).
+         * Non-terminal event types range: [200,300).
+         */
+    }
+
+    /** User typed over the selection. */
+    public static final int ACTION_OVERTYPE = 100;
+    /** User copied the selection. */
+    public static final int ACTION_COPY = 101;
+    /** User pasted over the selection. */
+    public static final int ACTION_PASTE = 102;
+    /** User cut the selection. */
+    public static final int ACTION_CUT = 103;
+    /** User shared the selection. */
+    public static final int ACTION_SHARE = 104;
+    /** User clicked the textAssist menu item. */
+    public static final int ACTION_SMART_SHARE = 105;
+    /** User dragged+dropped the selection. */
+    public static final int ACTION_DRAG = 106;
+    /** User abandoned the selection. */
+    public static final int ACTION_ABANDON = 107;
+    /** User performed an action on the selection. */
+    public static final int ACTION_OTHER = 108;
+
+    // Non-terminal actions.
+    /** User activated Select All */
+    public static final int ACTION_SELECT_ALL = 200;
+    /** User reset the smart selection. */
+    public static final int ACTION_RESET = 201;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
+            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
+            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET,
+            EVENT_SELECTION_STARTED, EVENT_SELECTION_MODIFIED,
+            EVENT_SMART_SELECTION_SINGLE, EVENT_SMART_SELECTION_MULTI,
+            EVENT_AUTO_SELECTION})
+    // NOTE: EventTypes declared here must be less than 100 to avoid colliding with the
+    // ActionTypes declared above.
+    public @interface EventType {
+        /*
+         * Range: 1 -> 99.
+         */
+    }
+
+    /** User started a new selection. */
+    public static final int EVENT_SELECTION_STARTED = 1;
+    /** User modified an existing selection. */
+    public static final int EVENT_SELECTION_MODIFIED = 2;
+    /** Smart selection triggered for a single token (word). */
+    public static final int EVENT_SMART_SELECTION_SINGLE = 3;
+    /** Smart selection triggered spanning multiple tokens (words). */
+    public static final int EVENT_SMART_SELECTION_MULTI = 4;
+    /** Something else other than User or the default TextClassifier triggered a selection. */
+    public static final int EVENT_AUTO_SELECTION = 5;
+
+    private final int mAbsoluteStart;
+    private final int mAbsoluteEnd;
+    private final @EventType int mEventType;
+    private final @EntityType String mEntityType;
+    @Nullable private final String mWidgetVersion;
+    private final String mPackageName;
+    private final String mWidgetType;
+
+    // These fields should only be set by creator of a SelectionEvent.
+    private String mSignature;
+    private long mEventTime;
+    private long mDurationSinceSessionStart;
+    private long mDurationSinceLastEvent;
+    private int mEventIndex;
+    private String mSessionId;
+    private int mStart;
+    private int mEnd;
+    private int mSmartStart;
+    private int mSmartEnd;
+
+    SelectionEvent(
+            int start, int end,
+            @EventType int eventType, @EntityType String entityType,
+            String signature, Logger.Config config) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        mAbsoluteStart = start;
+        mAbsoluteEnd = end;
+        mEventType = eventType;
+        mEntityType = Preconditions.checkNotNull(entityType);
+        mSignature = Preconditions.checkNotNull(signature);
+        Preconditions.checkNotNull(config);
+        mWidgetVersion = config.getWidgetVersion();
+        mPackageName = Preconditions.checkNotNull(config.getPackageName());
+        mWidgetType = Preconditions.checkNotNull(config.getWidgetType());
+    }
+
+    int getAbsoluteStart() {
+        return mAbsoluteStart;
+    }
+
+    int getAbsoluteEnd() {
+        return mAbsoluteEnd;
+    }
+
+    /**
+     * Returns the type of event that was triggered. e.g. {@link #ACTION_COPY}.
+     */
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Returns the type of entity that is associated with this event. e.g.
+     * {@link android.view.textclassifier.TextClassifier#TYPE_EMAIL}.
+     */
+    @EntityType
+    public String getEntityType() {
+        return mEntityType;
+    }
+
+    /**
+     * Returns the package name of the app that this event originated in.
+     */
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the type of widget that was involved in triggering this event.
+     */
+    public String getWidgetType() {
+        return mWidgetType;
+    }
+
+    /**
+     * Returns a string version info for the widget this event was triggered in.
+     */
+    public String getWidgetVersion() {
+        return mWidgetVersion;
+    }
+
+    /**
+     * Returns the signature of the text classifier result associated with this event.
+     */
+    public String getSignature() {
+        return mSignature;
+    }
+
+    SelectionEvent setSignature(String signature) {
+        mSignature = Preconditions.checkNotNull(signature);
+        return this;
+    }
+
+    /**
+     * Returns the time this event was triggered.
+     */
+    public long getEventTime() {
+        return mEventTime;
+    }
+
+    SelectionEvent setEventTime(long timeMs) {
+        mEventTime = timeMs;
+        return this;
+    }
+
+    /**
+     * Returns the duration in ms between when this event was triggered and when the first event in
+     * the selection session was triggered.
+     */
+    public long getDurationSinceSessionStart() {
+        return mDurationSinceSessionStart;
+    }
+
+    SelectionEvent setDurationSinceSessionStart(long durationMs) {
+        mDurationSinceSessionStart = durationMs;
+        return this;
+    }
+
+    /**
+     * Returns the duration in ms between when this event was triggered and when the previous event
+     * in the selection session was triggered.
+     */
+    public long getDurationSincePreviousEvent() {
+        return mDurationSinceLastEvent;
+    }
+
+    SelectionEvent setDurationSincePreviousEvent(long durationMs) {
+        this.mDurationSinceLastEvent = durationMs;
+        return this;
+    }
+
+    /**
+     * Returns the index (e.g. 1st event, 2nd event, etc.) of this event in the selection session.
+     */
+    public int getEventIndex() {
+        return mEventIndex;
+    }
+
+    SelectionEvent setEventIndex(int index) {
+        mEventIndex = index;
+        return this;
+    }
+
+    /**
+     * Returns the selection session id.
+     */
+    public String getSessionId() {
+        return mSessionId;
+    }
+
+    SelectionEvent setSessionId(String id) {
+        mSessionId = id;
+        return this;
+    }
+
+    /**
+     * Returns the start index of this events token relative to the index of the start selection
+     * event in the selection session.
+     */
+    public int getStart() {
+        return mStart;
+    }
+
+    SelectionEvent setStart(int start) {
+        mStart = start;
+        return this;
+    }
+
+    /**
+     * Returns the end index of this events token relative to the index of the start selection
+     * event in the selection session.
+     */
+    public int getEnd() {
+        return mEnd;
+    }
+
+    SelectionEvent setEnd(int end) {
+        mEnd = end;
+        return this;
+    }
+
+    /**
+     * Returns the start index of this events token relative to the index of the smart selection
+     * event in the selection session.
+     */
+    public int getSmartStart() {
+        return mSmartStart;
+    }
+
+    SelectionEvent setSmartStart(int start) {
+        this.mSmartStart = start;
+        return this;
+    }
+
+    /**
+     * Returns the end index of this events token relative to the index of the smart selection
+     * event in the selection session.
+     */
+    public int getSmartEnd() {
+        return mSmartEnd;
+    }
+
+    SelectionEvent setSmartEnd(int end) {
+        mSmartEnd = end;
+        return this;
+    }
+
+    boolean isTerminal() {
+        switch (mEventType) {
+            case ACTION_OVERTYPE:  // fall through
+            case ACTION_COPY:  // fall through
+            case ACTION_PASTE:  // fall through
+            case ACTION_CUT:  // fall through
+            case ACTION_SHARE:  // fall through
+            case ACTION_SMART_SHARE:  // fall through
+            case ACTION_DRAG:  // fall through
+            case ACTION_ABANDON:  // fall through
+            case ACTION_OTHER:  // fall through
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US,
+        "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
+                + "widgetVersion=%s, packageName=%s, widgetType=%s, signature=%s, "
+                + "eventTime=%d, durationSinceSessionStart=%d, durationSinceLastEvent=%d, "
+                + "eventIndex=%d, sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
+                mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
+                mWidgetVersion, mPackageName, mWidgetType, mSignature,
+                mEventTime, mDurationSinceSessionStart, mDurationSinceLastEvent,
+                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d2cb70e..65deb3b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2106,6 +2106,18 @@
      * the default directory, and other processes must call this API to define
      * a unique suffix.
      * <p>
+     * This means that different processes in the same application cannot directly
+     * share WebView-related data, since the data directories must be distinct.
+     * Applications that use this API may have to explicitly pass data between
+     * processes. For example, login cookies may have to be copied from one
+     * process's cookie jar to the other using {@link CookieManager} if both
+     * processes' WebViews are intended to be logged in.
+     * <p>
+     * Most applications should simply ensure that all components of the app
+     * that rely on WebView are in the same process, to avoid needing multiple
+     * data directories. The {@link #disableWebView} method can be used to ensure
+     * that the other processes do not use WebView by accident in this case.
+     * <p>
      * This API must be called before any instances of WebView are created in
      * this process and before any other methods in the android.webkit package
      * are called by this process.
@@ -2126,10 +2138,14 @@
      * methods in the android.webkit package are used.
      * <p>
      * Applications with multiple processes may wish to call this in processes
-     * which are not intended to use WebView to prevent potential data directory
-     * conflicts (see {@link #setDataDirectorySuffix}) and to avoid accidentally
-     * incurring the memory usage of initializing WebView in long-lived
-     * processes which have no need for it.
+     * that are not intended to use WebView to avoid accidentally incurring
+     * the memory usage of initializing WebView in long-lived processes that
+     * have no need for it, and to prevent potential data directory conflicts
+     * (see {@link #setDataDirectorySuffix}).
+     * <p>
+     * For example, an audio player application with one process for its
+     * activities and another process for its playback service may wish to call
+     * this method in the playback service's {@link android.app.Service#onCreate}.
      *
      * @throws IllegalStateException if WebView has already been initialized
      *                               in the current process.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index e42217f..7a4c800 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -182,8 +182,6 @@
     /**
      * Forces the magnifier to update its content. It uses the previous coordinates passed to
      * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
-     *
-     * @hide
      */
     public void update() {
         if (mWindow.isShowing()) {
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 39c23b4..923699c 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -23,12 +23,12 @@
 import android.media.session.MediaController;
 import android.media.update.ApiLoader;
 import android.media.update.MediaControlView2Provider;
-import android.media.update.ViewProvider;
+import android.media.update.ViewGroupHelper;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+
 /**
  * A View that contains the controls for MediaPlayer2.
  * It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
@@ -61,7 +61,7 @@
  * TODO PUBLIC API
  * @hide
  */
-public class MediaControlView2 extends FrameLayout {
+public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
     /** @hide */
     @IntDef({
             BUTTON_PLAY_PAUSE,
@@ -140,8 +140,6 @@
      */
     public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
 
-    private final MediaControlView2Provider mProvider;
-
     public MediaControlView2(@NonNull Context context) {
         this(context, null);
     }
@@ -157,17 +155,12 @@
 
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        mProvider = ApiLoader.getProvider(context)
-                .createMediaControlView2(this, new SuperProvider());
-    }
-
-    /**
-     * @hide
-     */
-    public MediaControlView2Provider getProvider() {
-        return mProvider;
+        super((instance, superProvider, privateProvider) ->
+                ApiLoader.getProvider(context).createMediaControlView2(
+                        (MediaControlView2) instance, superProvider, privateProvider,
+                        attrs, defStyleAttr, defStyleRes),
+                context, attrs, defStyleAttr, defStyleRes);
+        mProvider.initialize(attrs, defStyleAttr, defStyleRes);
     }
 
     /**
@@ -230,82 +223,4 @@
     public long getTimeout() {
         return mProvider.getTimeout_impl();
     }
-
-    @Override
-    public void onVisibilityAggregated(boolean isVisible) {
-
-        mProvider.onVisibilityAggregated_impl(isVisible);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        mProvider.onAttachedToWindow_impl();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mProvider.onDetachedFromWindow_impl();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return mProvider.getAccessibilityClassName_impl();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mProvider.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        return mProvider.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public void onFinishInflate() {
-        mProvider.onFinishInflate_impl();
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        mProvider.setEnabled_impl(enabled);
-    }
-
-    private class SuperProvider implements ViewProvider {
-        @Override
-        public void onAttachedToWindow_impl() {
-            MediaControlView2.super.onAttachedToWindow();
-        }
-
-        @Override
-        public void onDetachedFromWindow_impl() {
-            MediaControlView2.super.onDetachedFromWindow();
-        }
-
-        @Override
-        public CharSequence getAccessibilityClassName_impl() {
-            return MediaControlView2.super.getAccessibilityClassName();
-        }
-
-        @Override
-        public boolean onTouchEvent_impl(MotionEvent ev) {
-            return MediaControlView2.super.onTouchEvent(ev);
-        }
-
-        @Override
-        public boolean onTrackballEvent_impl(MotionEvent ev) {
-            return MediaControlView2.super.onTrackballEvent(ev);
-        }
-
-        @Override
-        public void onFinishInflate_impl() {
-            MediaControlView2.super.onFinishInflate();
-        }
-
-        @Override
-        public void setEnabled_impl(boolean enabled) {
-            MediaControlView2.super.setEnabled(enabled);
-        }
-    }
 }
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 3bfa520..2e354c1 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -37,8 +37,8 @@
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
-import android.view.textclassifier.logging.SmartSelectionEventTracker;
-import android.view.textclassifier.logging.SmartSelectionEventTracker.SelectionEvent;
+import android.view.textclassifier.logging.Logger;
+import android.view.textclassifier.logging.SelectionEvent;
 import android.widget.Editor.SelectionModifierCursorController;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -65,6 +65,7 @@
 
     private static final String LOG_TAG = "SelectActionModeHelper";
 
+    // TODO: Make this a configurable flag.
     private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
 
     private final Editor mEditor;
@@ -172,7 +173,7 @@
     public void onSelectionDrag() {
         mSelectionTracker.onSelectionAction(
                 mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
-                SelectionEvent.ActionType.DRAG, mTextClassification);
+                SelectionEvent.ACTION_DRAG, mTextClassification);
     }
 
     public void onTextChanged(int start, int end) {
@@ -574,7 +575,7 @@
                     mSelectionEnd = editor.getTextView().getSelectionEnd();
                     mLogger.logSelectionAction(
                             textView.getSelectionStart(), textView.getSelectionEnd(),
-                            SelectionEvent.ActionType.RESET, null /* classification */);
+                            SelectionEvent.ACTION_RESET, null /* classification */);
                 }
                 return selected;
             }
@@ -583,7 +584,7 @@
 
         public void onTextChanged(int start, int end, TextClassification classification) {
             if (isSelectionStarted() && start == mSelectionStart && end == mSelectionEnd) {
-                onSelectionAction(start, end, SelectionEvent.ActionType.OVERTYPE, classification);
+                onSelectionAction(start, end, SelectionEvent.ACTION_OVERTYPE, classification);
             }
         }
 
@@ -622,7 +623,7 @@
                 if (mIsPending) {
                     mLogger.logSelectionAction(
                             mSelectionStart, mSelectionEnd,
-                            SelectionEvent.ActionType.ABANDON, null /* classification */);
+                            SelectionEvent.ACTION_ABANDON, null /* classification */);
                     mSelectionStart = mSelectionEnd = -1;
                     mIsPending = false;
                 }
@@ -649,22 +650,29 @@
         private static final String LOG_TAG = "SelectionMetricsLogger";
         private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+");
 
-        private final SmartSelectionEventTracker mDelegate;
+        private final Logger mLogger;
         private final boolean mEditTextLogger;
-        private final BreakIterator mWordIterator;
+        private final BreakIterator mTokenIterator;
         private int mStartIndex;
         private String mText;
 
         SelectionMetricsLogger(TextView textView) {
             Preconditions.checkNotNull(textView);
-            final @SmartSelectionEventTracker.WidgetType int widgetType = textView.isTextEditable()
-                    ? SmartSelectionEventTracker.WidgetType.EDITTEXT
-                    : (textView.isTextSelectable()
-                            ? SmartSelectionEventTracker.WidgetType.TEXTVIEW
-                            : SmartSelectionEventTracker.WidgetType.UNSELECTABLE_TEXTVIEW);
-            mDelegate = new SmartSelectionEventTracker(textView.getContext(), widgetType);
+            mLogger = textView.getTextClassifier().getLogger(
+                    new Logger.Config(textView.getContext(), getWidetType(textView), null));
             mEditTextLogger = textView.isTextEditable();
-            mWordIterator = BreakIterator.getWordInstance(textView.getTextLocale());
+            mTokenIterator = mLogger.getTokenIterator(textView.getTextLocale());
+        }
+
+        @Logger.WidgetType
+        private static String getWidetType(TextView textView) {
+            if (textView.isTextEditable()) {
+                return Logger.WIDGET_EDITTEXT;
+            }
+            if (textView.isTextSelectable()) {
+                return Logger.WIDGET_TEXTVIEW;
+            }
+            return Logger.WIDGET_UNSELECTABLE_TEXTVIEW;
         }
 
         public void logSelectionStarted(CharSequence text, int index) {
@@ -674,9 +682,9 @@
                 if (mText == null || !mText.contentEquals(text)) {
                     mText = text.toString();
                 }
-                mWordIterator.setText(mText);
+                mTokenIterator.setText(mText);
                 mStartIndex = index;
-                mDelegate.logEvent(SelectionEvent.selectionStarted(0));
+                mLogger.logSelectionStartedEvent(0);
             } catch (Exception e) {
                 // Avoid crashes due to logging.
                 Log.d(LOG_TAG, e.getMessage());
@@ -690,14 +698,14 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (selection != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1], selection));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1], selection);
                 } else if (classification != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1], classification));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1], classification);
                 } else {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1]));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1]);
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
@@ -714,11 +722,11 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (classification != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionAction(
-                            wordIndices[0], wordIndices[1], action, classification));
+                    mLogger.logSelectionActionEvent(
+                            wordIndices[0], wordIndices[1], action, classification);
                 } else {
-                    mDelegate.logEvent(SelectionEvent.selectionAction(
-                            wordIndices[0], wordIndices[1], action));
+                    mLogger.logSelectionActionEvent(
+                            wordIndices[0], wordIndices[1], action);
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
@@ -741,10 +749,10 @@
                 wordIndices[0] = countWordsBackward(start);
 
                 // For the selection start index, avoid counting a partial word backwards.
-                if (!mWordIterator.isBoundary(start)
+                if (!mTokenIterator.isBoundary(start)
                         && !isWhitespace(
-                        mWordIterator.preceding(start),
-                        mWordIterator.following(start))) {
+                        mTokenIterator.preceding(start),
+                        mTokenIterator.following(start))) {
                     // We counted a partial word. Remove it.
                     wordIndices[0]--;
                 }
@@ -766,7 +774,7 @@
             int wordCount = 0;
             int offset = from;
             while (offset > mStartIndex) {
-                int start = mWordIterator.preceding(offset);
+                int start = mTokenIterator.preceding(offset);
                 if (!isWhitespace(start, offset)) {
                     wordCount++;
                 }
@@ -780,7 +788,7 @@
             int wordCount = 0;
             int offset = from;
             while (offset < mStartIndex) {
-                int end = mWordIterator.following(offset);
+                int end = mTokenIterator.following(offset);
                 if (!isWhitespace(offset, end)) {
                     wordCount++;
                 }
@@ -1021,20 +1029,20 @@
     private static int getActionType(int menuItemId) {
         switch (menuItemId) {
             case TextView.ID_SELECT_ALL:
-                return SelectionEvent.ActionType.SELECT_ALL;
+                return SelectionEvent.ACTION_SELECT_ALL;
             case TextView.ID_CUT:
-                return SelectionEvent.ActionType.CUT;
+                return SelectionEvent.ACTION_CUT;
             case TextView.ID_COPY:
-                return SelectionEvent.ActionType.COPY;
+                return SelectionEvent.ACTION_COPY;
             case TextView.ID_PASTE:  // fall through
             case TextView.ID_PASTE_AS_PLAIN_TEXT:
-                return SelectionEvent.ActionType.PASTE;
+                return SelectionEvent.ACTION_PASTE;
             case TextView.ID_SHARE:
-                return SelectionEvent.ActionType.SHARE;
+                return SelectionEvent.ACTION_SHARE;
             case TextView.ID_ASSIST:
-                return SelectionEvent.ActionType.SMART_SHARE;
+                return SelectionEvent.ACTION_SMART_SHARE;
             default:
-                return SelectionEvent.ActionType.OTHER;
+                return SelectionEvent.ACTION_OTHER;
         }
     }
 
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 706b0ce..77670b3 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -50,6 +50,7 @@
 import com.android.internal.widget.NumericTextView;
 import com.android.internal.widget.NumericTextView.OnValueChangedListener;
 
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Calendar;
@@ -804,20 +805,56 @@
     private void updateHeaderSeparator() {
         final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
                 (mIs24Hour) ? "Hm" : "hm");
-        final String separatorText;
-        // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
-        final char[] hourFormats = {'H', 'h', 'K', 'k'};
-        int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats);
-        if (hIndex == -1) {
-            // Default case
-            separatorText = ":";
-        } else {
-            separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
-        }
+        final String separatorText = getHourMinSeparatorFromPattern(bestDateTimePattern);
         mSeparatorView.setText(separatorText);
         mTextInputPickerView.updateSeparator(separatorText);
     }
 
+    /**
+     * This helper method extracts the time separator from the {@code datetimePattern}.
+     *
+     * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":".
+     *
+     * See http://unicode.org/cldr/trac/browser/trunk/common/main
+     *
+     * @return Separator string. This is the character or set of quoted characters just after the
+     * hour marker in {@code dateTimePattern}. Returns a colon (:) if it can't locate the
+     * separator.
+     *
+     * @hide
+     */
+    private static String getHourMinSeparatorFromPattern(String dateTimePattern) {
+        final String defaultSeparator = ":";
+        boolean foundHourPattern = false;
+        for (int i = 0; i < dateTimePattern.length(); i++) {
+            switch (dateTimePattern.charAt(i)) {
+                // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats.
+                case 'H':
+                case 'h':
+                case 'K':
+                case 'k':
+                    foundHourPattern = true;
+                    continue;
+                case ' ': // skip spaces
+                    continue;
+                case '\'':
+                    if (!foundHourPattern) {
+                        continue;
+                    }
+                    SpannableStringBuilder quotedSubstring = new SpannableStringBuilder(
+                            dateTimePattern.substring(i));
+                    int quotedTextLength = DateFormat.appendQuotedText(quotedSubstring, 0);
+                    return quotedSubstring.subSequence(0, quotedTextLength).toString();
+                default:
+                    if (!foundHourPattern) {
+                        continue;
+                    }
+                    return Character.toString(dateTimePattern.charAt(i));
+            }
+        }
+        return defaultSeparator;
+    }
+
     static private int lastIndexOfAny(String str, char[] any) {
         final int lengthAny = any.length;
         if (lengthAny > 0) {
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 8404223..3cbba8d 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -28,11 +28,10 @@
 import android.media.session.PlaybackState;
 import android.media.update.ApiLoader;
 import android.media.update.VideoView2Provider;
-import android.media.update.ViewProvider;
+import android.media.update.ViewGroupHelper;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.View;
 
 import java.lang.annotation.Retention;
@@ -102,7 +101,7 @@
  *
  * @hide
  */
-public class VideoView2 extends FrameLayout {
+public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
     /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
@@ -125,8 +124,6 @@
      */
     public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
-    private final VideoView2Provider mProvider;
-
     public VideoView2(@NonNull Context context) {
         this(context, null);
     }
@@ -142,17 +139,12 @@
     public VideoView2(
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider(),
-                attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * @hide
-     */
-    public VideoView2Provider getProvider() {
-        return mProvider;
+        super((instance, superProvider, privateProvider) ->
+                ApiLoader.getProvider(context).createVideoView2(
+                        (VideoView2) instance, superProvider, privateProvider,
+                        attrs, defStyleAttr, defStyleRes),
+                context, attrs, defStyleAttr, defStyleRes);
+        mProvider.initialize(attrs, defStyleAttr, defStyleRes);
     }
 
     /**
@@ -497,76 +489,4 @@
          */
         void onCustomAction(String action, Bundle extras);
     }
-
-    @Override
-    protected void onAttachedToWindow() {
-        mProvider.onAttachedToWindow_impl();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mProvider.onDetachedFromWindow_impl();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return mProvider.getAccessibilityClassName_impl();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mProvider.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        return mProvider.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public void onFinishInflate() {
-        mProvider.onFinishInflate_impl();
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        mProvider.setEnabled_impl(enabled);
-    }
-
-    private class SuperProvider implements ViewProvider {
-        @Override
-        public void onAttachedToWindow_impl() {
-            VideoView2.super.onAttachedToWindow();
-        }
-
-        @Override
-        public void onDetachedFromWindow_impl() {
-            VideoView2.super.onDetachedFromWindow();
-        }
-
-        @Override
-        public CharSequence getAccessibilityClassName_impl() {
-            return VideoView2.super.getAccessibilityClassName();
-        }
-
-        @Override
-        public boolean onTouchEvent_impl(MotionEvent ev) {
-            return VideoView2.super.onTouchEvent(ev);
-        }
-
-        @Override
-        public boolean onTrackballEvent_impl(MotionEvent ev) {
-            return VideoView2.super.onTrackballEvent(ev);
-        }
-
-        @Override
-        public void onFinishInflate_impl() {
-            VideoView2.super.onFinishInflate();
-        }
-
-        @Override
-        public void setEnabled_impl(boolean enabled) {
-            VideoView2.super.setEnabled(enabled);
-        }
-    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7def876..40dcf25b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3880,7 +3880,8 @@
 
     public void addIsolatedUidLocked(int isolatedUid, int appUid) {
         mIsolatedUids.put(isolatedUid, appUid);
-        StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+        StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+                StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
         final Uid u = getUidStatsLocked(appUid);
         u.addIsolatedUid(isolatedUid);
     }
@@ -3904,7 +3905,8 @@
      */
     public void removeIsolatedUidLocked(int isolatedUid) {
         StatsLog.write(
-            StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+                StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+                isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
         final int idx = mIsolatedUids.indexOfKey(isolatedUid);
         if (idx >= 0) {
             final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -4255,10 +4257,12 @@
 
             if (wc != null) {
                 StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
-                        getPowerManagerWakeLockLevel(type), name, 1);
+                        getPowerManagerWakeLockLevel(type), name,
+                        StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
             } else {
                 StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
-                        getPowerManagerWakeLockLevel(type), name, 1);
+                        getPowerManagerWakeLockLevel(type), name,
+                        StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
             }
         }
     }
@@ -4298,10 +4302,12 @@
             getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
             if (wc != null) {
                 StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
-                        getPowerManagerWakeLockLevel(type), name, 0);
+                        getPowerManagerWakeLockLevel(type), name,
+                        StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
             } else {
                 StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
-                        getPowerManagerWakeLockLevel(type), name, 0);
+                        getPowerManagerWakeLockLevel(type), name,
+                        StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
             }
         }
     }
@@ -4426,7 +4432,8 @@
 
     public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
         StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                uid, null, name, historyName, 1);
+                uid, null, name, historyName,
+                StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
 
         uid = mapUid(uid);
         noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4439,7 +4446,8 @@
             final int uid = mapUid(workSource.get(i));
             noteLongPartialWakeLockStartInternal(name, historyName, uid);
             StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                    workSource.get(i), workSource.getName(i), name, historyName, 1);
+                    workSource.get(i), workSource.getName(i), name, historyName,
+                    StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
         }
 
         final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4450,7 +4458,8 @@
                 noteLongPartialWakeLockStartInternal(name, historyName, uid);
 
                 StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), name, historyName, 1);
+                        workChain.getUids(), workChain.getTags(), name, historyName,
+                        StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
             }
         }
     }
@@ -4470,8 +4479,8 @@
     }
 
     public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
-        StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                uid, null, name, historyName, 0);
+        StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+                name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
 
         uid = mapUid(uid);
         noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4484,7 +4493,8 @@
             final int uid = mapUid(workSource.get(i));
             noteLongPartialWakeLockFinishInternal(name, historyName, uid);
             StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                    workSource.get(i), workSource.getName(i), name, historyName, 0);
+                    workSource.get(i), workSource.getName(i), name, historyName,
+                    StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
         }
 
         final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4494,7 +4504,8 @@
                 final int uid = workChain.getAttributionUid();
                 noteLongPartialWakeLockFinishInternal(name, historyName, uid);
                 StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), name, historyName, 0);
+                        workChain.getUids(), workChain.getTags(), name, historyName,
+                        StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
             }
         }
     }
@@ -4518,7 +4529,8 @@
             long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
             SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
             timer.add(deltaUptime * 1000, 1); // time in in microseconds
-            StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+            StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+                    /* duration_usec */ deltaUptime * 1000);
             mLastWakeupReason = null;
         }
     }
@@ -4659,10 +4671,12 @@
         mGpsNesting++;
 
         if (workChain == null) {
-            StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1);
+            StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+                    StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
         } else {
             StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
-                    workChain.getUids(), workChain.getTags(), 1);
+                    workChain.getUids(), workChain.getTags(),
+                    StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
         }
 
         getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4683,10 +4697,11 @@
         }
 
         if (workChain == null) {
-            StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0);
+            StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+                    StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
         } else {
             StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
-                    workChain.getTags(), 0);
+                    workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
         }
 
         getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4941,7 +4956,9 @@
                 mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
             }
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+            StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+                    StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+                    StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -5545,16 +5562,19 @@
 
         if (workChain != null) {
             StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
-                    workChain.getUids(), workChain.getTags(), 1);
+                    workChain.getUids(), workChain.getTags(),
+                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
             if (isUnoptimized) {
                 StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), 1);
+                        workChain.getUids(), workChain.getTags(),
+                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
             }
         } else {
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
             if (isUnoptimized) {
                 StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
-                        1);
+                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -5594,16 +5614,19 @@
 
         if (workChain != null) {
             StatsLog.write(
-                    StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+                    StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
+                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
             if (isUnoptimized) {
                 StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), 0);
+                        workChain.getUids(), workChain.getTags(),
+                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
             }
         } else {
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
             if (isUnoptimized) {
                 StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
-                        0);
+                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -5656,7 +5679,8 @@
                     for (int j = 0; j < allWorkChains.size(); ++j) {
                         StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
                                 allWorkChains.get(j).getUids(),
-                                allWorkChains.get(j).getTags(), 0);
+                                allWorkChains.get(j).getTags(),
+                                StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
                     }
                     allWorkChains.clear();
                 }
@@ -5666,7 +5690,8 @@
                     for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
                         StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
                                 unoptimizedWorkChains.get(j).getUids(),
-                                unoptimizedWorkChains.get(j).getTags(), 0);
+                                unoptimizedWorkChains.get(j).getTags(),
+                                StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
                     }
                     unoptimizedWorkChains.clear();
                 }
@@ -6011,7 +6036,8 @@
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.get(i));
             noteFullWifiLockAcquiredLocked(uid);
-            StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+            StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+                    StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6021,7 +6047,8 @@
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteFullWifiLockAcquiredLocked(uid);
                 StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), 1);
+                        workChain.getUids(), workChain.getTags(),
+                        StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
             }
         }
     }
@@ -6031,7 +6058,8 @@
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.get(i));
             noteFullWifiLockReleasedLocked(uid);
-            StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+            StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+                    StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,7 +6069,8 @@
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteFullWifiLockReleasedLocked(uid);
                 StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), 0);
+                        workChain.getUids(), workChain.getTags(),
+                        StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
             }
         }
     }
@@ -6052,7 +6081,7 @@
             final int uid = mapUid(ws.get(i));
             noteWifiScanStartedLocked(uid);
             StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
-                    1);
+                    StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6062,7 +6091,7 @@
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteWifiScanStartedLocked(uid);
                 StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
-                        workChain.getTags(), 1);
+                        workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
             }
         }
     }
@@ -6073,7 +6102,7 @@
             final int uid = mapUid(ws.get(i));
             noteWifiScanStoppedLocked(uid);
             StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
-                    0);
+                    StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6083,7 +6112,8 @@
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteWifiScanStoppedLocked(uid);
                 StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(), 0);
+                        workChain.getUids(), workChain.getTags(),
+                        StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
             }
         }
     }
@@ -7035,7 +7065,8 @@
                 }
                 mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
                 StatsLog.write_non_chained(
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -7045,7 +7076,8 @@
                 mWifiMulticastEnabled = false;
                 mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
                 StatsLog.write_non_chained(
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7098,14 +7130,16 @@
 
         public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
             createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
         }
 
         public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
                 if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+                    StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+                            StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
                 }
             }
         }
@@ -7113,7 +7147,8 @@
         public void noteResetAudioLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+                StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+                        StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7127,7 +7162,8 @@
 
         public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
             createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
         }
 
         public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7135,7 +7171,7 @@
                 mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
                 if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
                     StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
-                            null, 0);
+                            null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
                 }
             }
         }
@@ -7144,7 +7180,7 @@
             if (mVideoTurnedOnTimer != null) {
                 mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
                 StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
-                        0);
+                        StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
             }
         }
 
@@ -7158,7 +7194,8 @@
 
         public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
             createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
         }
 
         public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7166,7 +7203,7 @@
                 mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
                 if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
                     StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                            0);
+                            StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
                 }
             }
         }
@@ -7174,7 +7211,8 @@
         public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
             if (mFlashlightTurnedOnTimer != null) {
                 mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
+                StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+                        StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7188,14 +7226,16 @@
 
         public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
             createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
         }
 
         public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
                 if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+                    StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+                            StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
                 }
             }
         }
@@ -7203,7 +7243,8 @@
         public void noteResetCameraLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+                StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+                        StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -9777,7 +9818,8 @@
             DualTimer t = mSyncStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
+                StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+                        StatsLog.SYNC_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -9786,7 +9828,8 @@
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
                 if (!t.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
+                    StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+                            StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
                 }
             }
         }
@@ -9796,7 +9839,7 @@
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
                 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                        name, 1);
+                        name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
             }
         }
 
@@ -9806,7 +9849,7 @@
                 t.stopRunningLocked(elapsedRealtimeMs);
                 if (!t.isRunningLocked()) { // only tell statsd if truly stopped
                     StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                            name, 0);
+                            name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
                 }
             }
             if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9919,7 +9962,7 @@
             t.startRunningLocked(elapsedRealtimeMs);
             if (sensor != Sensor.GPS) {
                 StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
-                        1);
+                        StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -9930,7 +9973,7 @@
                 t.stopRunningLocked(elapsedRealtimeMs);
                 if (sensor != Sensor.GPS) {
                     StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
-                             sensor, 0);
+                             sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
                 }
             }
         }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341..e69a360 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
     public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
     /** Do not enfore hidden API access restrictions. */
     public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+    /** Force generation of native debugging information for backtraces. */
+    public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 95bc352..eadefc9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -86,6 +86,7 @@
 import android.widget.FrameLayout;
 import android.widget.PopupWindow;
 
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -2216,7 +2217,7 @@
             elevation = dipToPx(elevation);
             mElevationAdjustedForStack = true;
         } else if (windowingMode == WINDOWING_MODE_PINNED) {
-            elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
+            elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
             mElevationAdjustedForStack = true;
         } else {
             mElevationAdjustedForStack = false;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ebb5f9f..f70d3c2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -132,6 +132,12 @@
     void clickQsTile(in ComponentName tile);
     void handleSystemKey(in int key);
 
+    /**
+     * Methods to show toast messages for screen pinning
+     */
+    void showPinningEnterExitToast(boolean entering);
+    void showPinningEscapeToast();
+
     void showShutdownUi(boolean isReboot, String reason);
 
     // Used to show the dialog when FingerprintService starts authentication
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index cb0b53c..adf4287 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -81,6 +81,12 @@
     void clickTile(in ComponentName tile);
     void handleSystemKey(in int key);
 
+    /**
+     * Methods to show toast messages for screen pinning
+     */
+    void showPinningEnterExitToast(boolean entering);
+    void showPinningEscapeToast();
+
     // Used to show the dialog when FingerprintService starts authentication
     void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
     // Used to hide the dialog when a finger is authenticated
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 2f2c747..433d14f 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -138,14 +138,14 @@
     public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
             Function<? super I, ? extends O> f) {
         if (isEmpty(cur)) return Collections.emptyList();
-        final ArrayList<O> result = new ArrayList<>();
+        List<O> result = null;
         for (int i = 0; i < cur.size(); i++) {
             O transformed = f.apply(cur.get(i));
             if (transformed != null) {
-                result.add(transformed);
+                result = add(result, transformed);
             }
         }
-        return result;
+        return emptyIfNull(result);
     }
 
     /**
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 71550be..f8885a2 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -160,7 +160,7 @@
                     final File file = new File(mBasePath, name);
                     final FileInputStream is = new FileInputStream(file);
                     try {
-                        Streams.copy(is, zos);
+                        FileUtils.copy(is, zos);
                     } finally {
                         IoUtils.closeQuietly(is);
                     }
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 32a7a2d..7a248f2 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -118,6 +118,7 @@
     private float mInProgressY = -1;
 
     private long mAnimatingPeriodStart;
+    private long[] mLineFadeStart = new long[9];
 
     private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
     private boolean mInputEnabled = true;
@@ -596,12 +597,14 @@
     }
 
     /**
-     * Clear the pattern lookup table.
+     * Clear the pattern lookup table. Also reset the line fade start times for
+     * the next attempt.
      */
     private void clearPatternDrawLookup() {
         for (int i = 0; i < 3; i++) {
             for (int j = 0; j < 3; j++) {
                 mPatternDrawLookup[i][j] = false;
+                mLineFadeStart[i+j] = 0;
             }
         }
     }
@@ -1136,7 +1139,8 @@
             boolean anyCircles = false;
             float lastX = 0f;
             float lastY = 0f;
-            for (int i = 0; i < count; i++) {
+            long elapsedRealtime = SystemClock.elapsedRealtime();
+           for (int i = 0; i < count; i++) {
                 Cell cell = pattern.get(i);
 
                 // only draw the part of the pattern stored in
@@ -1147,16 +1151,26 @@
                 }
                 anyCircles = true;
 
+                if (mLineFadeStart[i] == 0) {
+                  mLineFadeStart[i] = SystemClock.elapsedRealtime();
+                }
+
                 float centerX = getCenterXForColumn(cell.column);
                 float centerY = getCenterYForRow(cell.row);
                 if (i != 0) {
+                   // Set this line segment to slowly fade over the next second.
+                   int lineFadeVal = (int) Math.min((elapsedRealtime -
+                           mLineFadeStart[i])/2f, 255f);
+
                     CellState state = mCellStates[cell.row][cell.column];
                     currentPath.rewind();
                     currentPath.moveTo(lastX, lastY);
                     if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
                         currentPath.lineTo(state.lineEndX, state.lineEndY);
+                        mPathPaint.setAlpha((int) 255 - lineFadeVal );
                     } else {
                         currentPath.lineTo(centerX, centerY);
+                        mPathPaint.setAlpha((int) 255 - lineFadeVal );
                     }
                     canvas.drawPath(currentPath, mPathPaint);
                 }
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index e3436e8..fb18669 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,6 +35,7 @@
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -112,6 +113,8 @@
     private static final String SHUTDOWN_METRICS_FILE = "/data/system/shutdown-metrics.txt";
 
     private static final String SHUTDOWN_TRON_METRICS_PREFIX = "shutdown_";
+    private static final String METRIC_SYSTEM_SERVER = "shutdown_system_server";
+    private static final String METRIC_SHUTDOWN_TIME_START = "begin_shutdown";
 
     @Override
     public void onReceive(final Context context, Intent intent) {
@@ -401,6 +404,10 @@
             }
         }
         if (!TextUtils.isEmpty(metricsStr)) {
+            String reboot = null;
+            String reason = null;
+            String start_time = null;
+            String duration = null;
             String[] array = metricsStr.split(",");
             for (String keyValueStr : array) {
                 String[] keyValue = keyValueStr.split(":");
@@ -411,8 +418,19 @@
                 // Ignore keys that are not indended for tron
                 if (keyValue[0].startsWith(SHUTDOWN_TRON_METRICS_PREFIX)) {
                     logTronShutdownMetric(keyValue[0], keyValue[1]);
+                    if (keyValue[0].equals(METRIC_SYSTEM_SERVER)) {
+                        duration = keyValue[1];
+                    }
+                }
+                if (keyValue[0].equals("reboot")) {
+                    reboot = keyValue[1];
+                } else if (keyValue[0].equals("reason")) {
+                    reason = keyValue[1];
+                } else if (keyValue[0].equals(METRIC_SHUTDOWN_TIME_START)) {
+                    start_time = keyValue[1];
                 }
             }
+            logStatsdShutdownAtom(reboot, reason, start_time, duration);
         }
         metricsFile.delete();
     }
@@ -430,6 +448,52 @@
         }
     }
 
+    private static void logStatsdShutdownAtom(
+            String rebootStr, String reasonStr, String startStr, String durationStr) {
+        boolean reboot = false;
+        String reason = "<EMPTY>";
+        long start = 0;
+        long duration = 0;
+
+        if (rebootStr != null) {
+            if (rebootStr.equals("y")) {
+                reboot = true;
+            } else if (!rebootStr.equals("n")) {
+                Slog.e(TAG, "Unexpected value for reboot : " + rebootStr);
+            }
+        } else {
+            Slog.e(TAG, "No value received for reboot");
+        }
+
+        if (reasonStr != null) {
+            reason = reasonStr;
+        } else {
+            Slog.e(TAG, "No value received for shutdown reason");
+        }
+
+        if (startStr != null) {
+            try {
+                start = Long.parseLong(startStr);
+            } catch (NumberFormatException e) {
+                Slog.e(TAG, "Cannot parse shutdown start time: " + startStr);
+            }
+        } else {
+            Slog.e(TAG, "No value received for shutdown start time");
+        }
+
+        if (durationStr != null) {
+            try {
+                duration = Long.parseLong(durationStr);
+            } catch (NumberFormatException e) {
+                Slog.e(TAG, "Cannot parse shutdown duration: " + startStr);
+            }
+        } else {
+            Slog.e(TAG, "No value received for shutdown duration");
+        }
+
+        StatsLog.write(StatsLog.SHUTDOWN_SEQUENCE_REPORTED, reboot, reason, start, duration);
+    }
+
     private static void logFsShutdownTime() {
         File f = null;
         for (String fileName : LAST_KMSG_FILES) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 93f95c6..33f80ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -164,6 +164,7 @@
         "android_media_DeviceCallback.cpp",
         "android_media_JetPlayer.cpp",
         "android_media_MediaMetricsJNI.cpp",
+        "android_media_MicrophoneInfo.cpp",
         "android_media_RemoteDisplay.cpp",
         "android_media_ToneGenerator.cpp",
         "android_hardware_Camera.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bf7a7794..d202173 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -104,6 +104,7 @@
 extern int register_android_media_AudioRecord(JNIEnv *env);
 extern int register_android_media_AudioSystem(JNIEnv *env);
 extern int register_android_media_AudioTrack(JNIEnv *env);
+extern int register_android_media_MicrophoneInfo(JNIEnv *env);
 extern int register_android_media_JetPlayer(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
 
@@ -765,18 +766,17 @@
 
     /*
      * Enable debugging only for apps forked from zygote.
-     * Set suspend=y to pause during VM init and use android ADB transport.
      */
     if (zygote) {
+      // Set the JDWP provider and required arguments. By default let the runtime choose how JDWP is
+      // implemented. When this is not set the runtime defaults to not allowing JDWP.
       addOption("-XjdwpOptions:suspend=n,server=y");
+      parseRuntimeOption("dalvik.vm.jdwp-provider",
+                         jdwpProviderBuf,
+                         "-XjdwpProvider:",
+                         "default");
     }
 
-    // Set the JDWP provider. By default let the runtime choose.
-    parseRuntimeOption("dalvik.vm.jdwp-provider",
-                       jdwpProviderBuf,
-                       "-XjdwpProvider:",
-                       "default");
-
     parseRuntimeOption("dalvik.vm.lockprof.threshold",
                        lockProfThresholdBuf,
                        "-Xlockprofthreshold:");
@@ -1461,6 +1461,7 @@
     REG_JNI(register_android_media_AudioSystem),
     REG_JNI(register_android_media_AudioTrack),
     REG_JNI(register_android_media_JetPlayer),
+    REG_JNI(register_android_media_MicrophoneInfo),
     REG_JNI(register_android_media_RemoteDisplay),
     REG_JNI(register_android_media_ToneGenerator),
 
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index c88cf5c..ba56d59 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -26,10 +26,11 @@
 #include <SkPictureRecorder.h>
 #include <hwui/AnimatedImageDrawable.h>
 #include <hwui/Canvas.h>
+#include <utils/Looper.h>
 
 using namespace android;
 
-static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID;
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
 
 // Note: jpostProcess holds a handle to the ImageDecoder.
 static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -123,9 +124,9 @@
     return drawable->start();
 }
 
-static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    drawable->stop();
+    return drawable->stop();
 }
 
 // Java's LOOP_INFINITE relies on this being the same.
@@ -137,33 +138,63 @@
     drawable->setRepetitionCount(loopCount);
 }
 
-class JniAnimationEndListener : public OnAnimationEndListener {
+class InvokeListener : public MessageHandler {
 public:
-    JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
+    InvokeListener(JNIEnv* env, jobject javaObject) {
         LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
-        mJavaObject = env->NewGlobalRef(javaObject);
+        // Hold a weak reference to break a cycle that would prevent GC.
+        mWeakRef = env->NewWeakGlobalRef(javaObject);
     }
 
-    ~JniAnimationEndListener() override {
+    ~InvokeListener() override {
         auto* env = get_env_or_die(mJvm);
-        env->DeleteGlobalRef(mJavaObject);
+        env->DeleteWeakGlobalRef(mWeakRef);
     }
 
-    void onAnimationEnd() override {
+    virtual void handleMessage(const Message&) override {
         auto* env = get_env_or_die(mJvm);
-        env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID);
+        jobject localRef = env->NewLocalRef(mWeakRef);
+        if (localRef) {
+            env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+        }
     }
 
 private:
     JavaVM* mJvm;
-    jobject mJavaObject;
+    jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+    JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+        mListener = new InvokeListener(env, javaObject);
+        mLooper = std::move(looper);
+    }
+
+    void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+    sp<InvokeListener> mListener;
+    sp<Looper> mLooper;
 };
 
 static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
                                                              jlong nativePtr, jobject jdrawable) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>(
-                new JniAnimationEndListener(env, jdrawable)));
+    if (!jdrawable) {
+        drawable->setOnAnimationEndListener(nullptr);
+    } else {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            doThrowISE(env,
+                       "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+                       "looper!");
+            return;
+        }
+
+        drawable->setOnAnimationEndListener(
+                std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+    }
 }
 
 static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -186,7 +217,7 @@
     { "nSetColorFilter",     "(JJ)V",                                                        (void*) AnimatedImageDrawable_nSetColorFilter },
     { "nIsRunning",          "(J)Z",                                                         (void*) AnimatedImageDrawable_nIsRunning },
     { "nStart",              "(J)Z",                                                         (void*) AnimatedImageDrawable_nStart },
-    { "nStop",               "(J)V",                                                         (void*) AnimatedImageDrawable_nStop },
+    { "nStop",               "(J)Z",                                                         (void*) AnimatedImageDrawable_nStop },
     { "nSetLoopCount",       "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetLoopCount },
     { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
     { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
@@ -195,7 +226,7 @@
 
 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
     jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
-    gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V");
+    gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
 
     return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
             gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f0..937b3ff 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,11 +44,9 @@
 
 struct NativeFamilyBuilder {
     NativeFamilyBuilder(uint32_t langId, int variant)
-        : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
-          allowUnsupportedFont(false) {}
+        : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {}
     uint32_t langId;
     minikin::FontFamily::Variant variant;
-    bool allowUnsupportedFont;
     std::vector<minikin::Font> fonts;
     std::vector<minikin::FontVariation> axes;
 };
@@ -70,22 +68,17 @@
     }
     std::unique_ptr<NativeFamilyBuilder> builder(
             reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+    if (builder->fonts.empty()) {
+        return 0;
+    }
     std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
             builder->langId, builder->variant, std::move(builder->fonts));
-    if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+    if (family->getCoverage().length() == 0) {
         return 0;
     }
     return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
 }
 
-static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
-    if (builderPtr == 0) {
-        return;
-    }
-    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
-    builder->allowUnsupportedFont = true;
-}
-
 static void FontFamily_abort(jlong builderPtr) {
     NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
     delete builder;
@@ -270,7 +263,6 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nInitBuilder",          "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
     { "nCreateFamily",         "(J)J", (void*)FontFamily_create },
-    { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
     { "nAbort",                "(J)V", (void*)FontFamily_abort },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
     { "nAddFont",              "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 743a7ef..e2ce1a4 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -74,7 +74,8 @@
 
     // FIXME: Avoid parsing the whole image?
     const bool animated = codec->getFrameCount() > 1;
-    decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
+    decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
+            SkAndroidCodec::ExifOrientationBehavior::kRespect);
     if (!decoder->mCodec.get()) {
         doThrowIOE(env, "Could not create AndroidCodec");
         return nullptr;
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index c79f5bd..12da273 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -36,6 +36,7 @@
 #define ENCODING_AAC_ELD        15
 #define ENCODING_AAC_XHE        16
 #define ENCODING_AC4            17
+#define ENCODING_E_AC3_JOC      18
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -80,6 +81,8 @@
         return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
     case ENCODING_AC4:
         return AUDIO_FORMAT_AC4;
+    // case ENCODING_E_AC3_JOC:  // FIXME Not defined on the native side yet
+    //     return AUDIO_FORMAT_E_AC3_JOC;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
     default:
@@ -130,6 +133,8 @@
     //    return ENCODING_AAC_XHE;
     case AUDIO_FORMAT_AC4:
         return ENCODING_AC4;
+    // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet
+    //     return ENCODING_E_AC3_JOC;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index ebd16c7..375d68b 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -25,6 +25,8 @@
 
 #include <utils/Log.h>
 #include <media/AudioRecord.h>
+#include <media/MicrophoneInfo.h>
+#include <vector>
 
 #include <nativehelper/ScopedUtfChars.h>
 
@@ -32,6 +34,7 @@
 #include "android_media_AudioErrors.h"
 #include "android_media_DeviceCallback.h"
 #include "android_media_MediaMetricsJNI.h"
+#include "android_media_MicrophoneInfo.h"
 
 // ----------------------------------------------------------------------------
 
@@ -41,6 +44,11 @@
 static const char* const kClassPathName = "android/media/AudioRecord";
 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
 
+static jclass gArrayListClass;
+static struct {
+    jmethodID add;
+} gArrayListMethods;
+
 struct audio_record_fields_t {
     // these fields provide access from C++ to the...
     jmethodID postNativeEventInJava; //... event post callback method
@@ -785,6 +793,46 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
+        jobject thiz, jobject jActiveMicrophones) {
+    if (jActiveMicrophones == NULL) {
+        ALOGE("jActiveMicrophones is null");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
+        ALOGE("getActiveMicrophones not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    std::vector<media::MicrophoneInfo> activeMicrophones;
+    status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
+        jStatus = nativeToJavaStatus(status);
+        return jStatus;
+    }
+
+    for (size_t i = 0; i < activeMicrophones.size(); i++) {
+        jobject jMicrophoneInfo;
+        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
+        env->DeleteLocalRef(jMicrophoneInfo);
+    }
+    return jStatus;
+}
+
+// ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,               signature,  funcPtr
@@ -824,6 +872,8 @@
                                         (void *)android_media_AudioRecord_disableDeviceCallback},
     {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
                                        (void *)android_media_AudioRecord_get_timestamp},
+    {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
+                                        (void *)android_media_AudioRecord_get_active_microphones},
 };
 
 // field names found in android/media/AudioRecord.java
@@ -873,6 +923,10 @@
     javaAudioTimestampFields.fieldNanoTime =
             GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
 
+    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 376a797..adaff1f 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -21,17 +21,20 @@
 #include <utils/Log.h>
 
 #include <sstream>
+#include <vector>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
 
 #include <media/AudioSystem.h>
 #include <media/AudioPolicy.h>
+#include <media/MicrophoneInfo.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
+#include "android_media_MicrophoneInfo.h"
 
 // ----------------------------------------------------------------------------
 
@@ -143,7 +146,6 @@
     jfieldID    mSource;
 } gAudioAttributesFields;
 
-
 static const char* const kEventHandlerClassPathName =
         "android/media/AudioPortEventHandler";
 static struct {
@@ -1158,7 +1160,6 @@
     return jStatus;
 }
 
-
 static jint
 android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
                                          jobject jPorts, jintArray jGeneration)
@@ -1789,6 +1790,45 @@
     return AudioSystem::isOffloadSupported(format);
 }
 
+static jint
+android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMicrophonesInfo)
+{
+    ALOGV("getMicrophones");
+
+    if (jMicrophonesInfo == NULL) {
+        ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) {
+        ALOGE("getMicrophones not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    jint jStatus;
+    std::vector<media::MicrophoneInfo> microphones;
+    status_t status = AudioSystem::getMicrophones(&microphones);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "AudioSystem::getMicrophones error %d", status);
+        jStatus = nativeToJavaStatus(status);
+        return jStatus;
+    }
+    if (microphones.size() == 0) {
+        jStatus = (jint)AUDIO_JAVA_SUCCESS;
+        return jStatus;
+    }
+    for (size_t i = 0; i < microphones.size(); i++) {
+        jobject jMicrophoneInfo;
+        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &microphones[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jMicrophonesInfo, gArrayListMethods.add, jMicrophoneInfo);
+        env->DeleteLocalRef(jMicrophoneInfo);
+    }
+
+    return jStatus;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -1843,6 +1883,7 @@
     {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
     {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
     {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
+    {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
 };
 
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index afbc579..61a22c1 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1262,6 +1262,18 @@
     return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
 }
 
+static int android_media_AudioTrack_setPresentation(
+                                JNIEnv *env,  jobject thiz, jint presentationId, jint programId) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "AudioTrack not initialized");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+
+    return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
+}
+
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
@@ -1333,6 +1345,7 @@
     {"native_getVolumeShaperState",
             "(I)Landroid/media/VolumeShaper$State;",
                                         (void *)android_media_AudioTrack_get_volume_shaper_state},
+    {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
 };
 
 
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
new file mode 100644
index 0000000..9198cbe
--- /dev/null
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android_media_MicrophoneInfo.h"
+#include "android_media_AudioErrors.h"
+#include "core_jni_helpers.h"
+
+using namespace android;
+
+static jclass gArrayListClass;
+static jmethodID gArrayListCstor;
+static struct {
+    jmethodID add;
+} gArrayListMethods;
+
+static jclass gFloatClass;
+static jmethodID gFloatCstor;
+
+static jclass gFloatArrayClass;
+
+static jclass gIntegerClass;
+static jmethodID gIntegerCstor;
+
+static jclass gMicrophoneInfoClass;
+static jmethodID gMicrophoneInfoCstor;
+
+static jclass gMicrophoneInfoCoordinateClass;
+static jmethodID gMicrophoneInfoCoordinateCstor;
+
+static jclass gPairClass;
+static jmethodID gPairCstor;
+
+namespace android {
+
+jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
+        const media::MicrophoneInfo *microphoneInfo)
+{
+    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+    jstring jDeviceId = NULL;
+    jstring jAddress = NULL;
+    jobject jGeometricLocation = NULL;
+    jobject jOrientation = NULL;
+    jobject jFrequencyResponses = NULL;
+    jobject jChannelMappings = NULL;
+
+    jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string());
+    jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string());
+    if (microphoneInfo->getGeometricLocation().size() != 3 ||
+            microphoneInfo->getOrientation().size() != 3) {
+        jStatus = nativeToJavaStatus(BAD_VALUE);
+        goto exit;
+    }
+    jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
+                                        gMicrophoneInfoCoordinateCstor,
+                                        NULL,
+                                        microphoneInfo->getGeometricLocation()[0],
+                                        microphoneInfo->getGeometricLocation()[1],
+                                        microphoneInfo->getGeometricLocation()[2]);
+    jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
+                                  gMicrophoneInfoCoordinateCstor,
+                                  NULL,
+                                  microphoneInfo->getOrientation()[0],
+                                  microphoneInfo->getOrientation()[1],
+                                  microphoneInfo->getOrientation()[2]);
+    // Create a list of Pair for frequency response.
+    if (microphoneInfo->getFrequencyResponses().size() != 2 ||
+            microphoneInfo->getFrequencyResponses()[0].size() !=
+                    microphoneInfo->getFrequencyResponses()[1].size()) {
+        jStatus = nativeToJavaStatus(BAD_VALUE);
+        goto exit;
+    }
+    jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor);
+    for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) {
+        jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor,
+                                            microphoneInfo->getFrequencyResponses()[0][i]);
+        jobject jResponse = env->NewObject(gFloatClass, gFloatCstor,
+                                          microphoneInfo->getFrequencyResponses()[1][i]);
+        jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse);
+        env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse);
+        env->DeleteLocalRef(jFrequency);
+        env->DeleteLocalRef(jResponse);
+        env->DeleteLocalRef(jFrequencyResponse);
+    }
+    // Create a list of Pair for channel mapping.
+    if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) {
+        jStatus = nativeToJavaStatus(BAD_VALUE);
+        goto exit;
+    }
+    jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor);
+    for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) {
+        int channelMappingType = microphoneInfo->getChannelMapping()[i];
+        if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
+            jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i);
+            jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor,
+                                                         channelMappingType);
+            jobject jChannelMapping = env->NewObject(gPairClass, gPairCstor,
+                                                     jChannelIndex, jChannelMappingType);
+            env->CallBooleanMethod(jChannelMappings, gArrayListMethods.add, jChannelMapping);
+            env->DeleteLocalRef(jChannelIndex);
+            env->DeleteLocalRef(jChannelMappingType);
+            env->DeleteLocalRef(jChannelMapping);
+        }
+    }
+    *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId,
+                                      microphoneInfo->getType(), jAddress,
+                                      microphoneInfo->getDeviceLocation(),
+                                      microphoneInfo->getDeviceGroup(),
+                                      microphoneInfo->getIndexInTheGroup(),
+                                      jGeometricLocation, jOrientation,
+                                      jFrequencyResponses, jChannelMappings,
+                                      microphoneInfo->getSensitivity(),
+                                      microphoneInfo->getMaxSpl(),
+                                      microphoneInfo->getMinSpl(),
+                                      microphoneInfo->getDirectionality());
+
+exit:
+    if (jDeviceId != NULL) {
+        env->DeleteLocalRef(jDeviceId);
+    }
+    if (jAddress != NULL) {
+        env->DeleteLocalRef(jAddress);
+    }
+    if (jFrequencyResponses != NULL) {
+        env->DeleteLocalRef(jFrequencyResponses);
+    }
+    if (jChannelMappings != NULL) {
+        env->DeleteLocalRef(jChannelMappings);
+    }
+    if (jGeometricLocation != NULL) {
+        env->DeleteLocalRef(jGeometricLocation);
+    }
+    if (jOrientation != NULL) {
+        env->DeleteLocalRef(jOrientation);
+    }
+    return jStatus;
+}
+
+}
+
+int register_android_media_MicrophoneInfo(JNIEnv *env)
+{
+    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+    gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
+    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+    jclass floatClass = FindClassOrDie(env, "java/lang/Float");
+    gFloatClass = MakeGlobalRefOrDie(env, floatClass);
+    gFloatCstor = GetMethodIDOrDie(env, floatClass, "<init>", "(F)V");
+
+    jclass floatArrayClass = FindClassOrDie(env, "[F");
+    gFloatArrayClass = MakeGlobalRefOrDie(env, floatArrayClass);
+
+    jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+    gIntegerClass = MakeGlobalRefOrDie(env, integerClass);
+    gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V");
+
+    jclass microphoneInfoClass = FindClassOrDie(env, "android/media/MicrophoneInfo");
+    gMicrophoneInfoClass = MakeGlobalRefOrDie(env, microphoneInfoClass);
+    gMicrophoneInfoCstor = GetMethodIDOrDie(env, microphoneInfoClass, "<init>",
+            "(Ljava/lang/String;ILjava/lang/String;IIILandroid/media/MicrophoneInfo$Coordinate3F;Landroid/media/MicrophoneInfo$Coordinate3F;Ljava/util/List;Ljava/util/List;FFFI)V");
+
+    jclass microphoneInfoCoordinateClass = FindClassOrDie(
+            env, "android/media/MicrophoneInfo$Coordinate3F");
+    gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass);
+    gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>",
+           "(Landroid/media/MicrophoneInfo;FFF)V");
+
+    jclass pairClass = FindClassOrDie(env, "android/util/Pair");
+    gPairClass = MakeGlobalRefOrDie(env, pairClass);
+    gPairCstor = GetMethodIDOrDie(env, pairClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
+
+    return 0;
+}
diff --git a/core/jni/android_media_MicrophoneInfo.h b/core/jni/android_media_MicrophoneInfo.h
new file mode 100644
index 0000000..241b6d0
--- /dev/null
+++ b/core/jni/android_media_MicrophoneInfo.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MICROPHONEINFO_H
+#define ANDROID_MEDIA_MICROPHONEINFO_H
+
+#include <system/audio.h>
+#include <media/MicrophoneInfo.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Conversion from C++ MicrophoneInfo object to Java MicrophoneInfo object
+
+extern jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
+        const media::MicrophoneInfo *microphoneInfo);
+} // namespace android
+
+#endif
\ No newline at end of file
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 37ff8c8..8770d78 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -174,8 +174,25 @@
     return renderNode->stagingProperties().hasShadow();
 }
 
-static jboolean android_view_RenderNode_setShadowColor(jlong renderNodePtr, jint shadowColor) {
-    return SET_AND_DIRTY(setShadowColor, static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+static jboolean android_view_RenderNode_setSpotShadowColor(jlong renderNodePtr, jint shadowColor) {
+    return SET_AND_DIRTY(setSpotShadowColor,
+            static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getSpotShadowColor(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getSpotShadowColor();
+}
+
+static jboolean android_view_RenderNode_setAmbientShadowColor(jlong renderNodePtr,
+        jint shadowColor) {
+    return SET_AND_DIRTY(setAmbientShadowColor,
+            static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getAmbientShadowColor(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getAmbientShadowColor();
 }
 
 static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr,
@@ -575,7 +592,10 @@
     { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
     { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
     { "nHasShadow",            "(J)Z",   (void*) android_view_RenderNode_hasShadow },
-    { "nSetShadowColor",       "(JI)Z",  (void*) android_view_RenderNode_setShadowColor },
+    { "nSetSpotShadowColor",   "(JI)Z",  (void*) android_view_RenderNode_setSpotShadowColor },
+    { "nGetSpotShadowColor",   "(J)I",   (void*) android_view_RenderNode_getSpotShadowColor },
+    { "nSetAmbientShadowColor","(JI)Z",  (void*) android_view_RenderNode_setAmbientShadowColor },
+    { "nGetAmbientShadowColor","(J)I",   (void*) android_view_RenderNode_getAmbientShadowColor },
     { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
     { "nSetRevealClip",        "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 13e6fcd..32945bf 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -141,32 +141,45 @@
   errno = saved_errno;
 }
 
-// Configures the SIGCHLD handler for the zygote process. This is configured
-// very late, because earlier in the runtime we may fork() and exec()
-// other processes, and we want to waitpid() for those rather than
+// Configures the SIGCHLD/SIGHUP handlers for the zygote process. This is
+// configured very late, because earlier in the runtime we may fork() and
+// exec() other processes, and we want to waitpid() for those rather than
 // have them be harvested immediately.
 //
+// Ignore SIGHUP because all processes forked by the zygote are in the same
+// process group as the zygote and we don't want to be notified if we become
+// an orphaned group and have one or more stopped processes. This is not a
+// theoretical concern :
+// - we can become an orphaned group if one of our direct descendants forks
+//   and is subsequently killed before its children.
+// - crash_dump routinely STOPs the process it's tracing.
+//
+// See issues b/71965619 and b/25567761 for further details.
+//
 // This ends up being called repeatedly before each fork(), but there's
 // no real harm in that.
-static void SetSigChldHandler() {
-  struct sigaction sa;
-  memset(&sa, 0, sizeof(sa));
-  sa.sa_handler = SigChldHandler;
+static void SetSignalHandlers() {
+  struct sigaction sig_chld = {};
+  sig_chld.sa_handler = SigChldHandler;
 
-  int err = sigaction(SIGCHLD, &sa, NULL);
-  if (err < 0) {
+  if (sigaction(SIGCHLD, &sig_chld, NULL) < 0) {
     ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
   }
+
+  struct sigaction sig_hup = {};
+  sig_hup.sa_handler = SIG_IGN;
+  if (sigaction(SIGHUP, &sig_hup, NULL) < 0) {
+    ALOGW("Error setting SIGHUP handler: %s", strerror(errno));
+  }
 }
 
 // Sets the SIGCHLD handler back to default behavior in zygote children.
-static void UnsetSigChldHandler() {
+static void UnsetChldSignalHandler() {
   struct sigaction sa;
   memset(&sa, 0, sizeof(sa));
   sa.sa_handler = SIG_DFL;
 
-  int err = sigaction(SIGCHLD, &sa, NULL);
-  if (err < 0) {
+  if (sigaction(SIGCHLD, &sa, NULL) < 0) {
     ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
   }
 }
@@ -505,7 +518,7 @@
                                      bool is_system_server, jintArray fdsToClose,
                                      jintArray fdsToIgnore,
                                      jstring instructionSet, jstring dataDir) {
-  SetSigChldHandler();
+  SetSignalHandlers();
 
   sigset_t sigchld;
   sigemptyset(&sigchld);
@@ -682,7 +695,8 @@
     delete se_info;
     delete se_name;
 
-    UnsetSigChldHandler();
+    // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+    UnsetChldSignalHandler();
 
     env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                               is_system_server, instructionSet);
diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto
index 4d748e8..1e8ace4 100644
--- a/core/proto/android/app/window_configuration.proto
+++ b/core/proto/android/app/window_configuration.proto
@@ -21,9 +21,12 @@
 package android.app;
 
 import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 /** Proto representation for WindowConfiguration.java class. */
 message WindowConfigurationProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   optional .android.graphics.RectProto app_bounds = 1;
   optional int32 windowing_mode = 2;
   optional int32 activity_type = 3;
diff --git a/core/proto/android/media/audioattributes.proto b/core/proto/android/media/audioattributes.proto
index 860d608..ef04720 100644
--- a/core/proto/android/media/audioattributes.proto
+++ b/core/proto/android/media/audioattributes.proto
@@ -20,15 +20,19 @@
 
 package android.media;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 /**
  * An android.media.AudioAttributes object.
  */
 message AudioAttributesProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional Usage usage = 1;
     optional ContentType content_type = 2;
     // Bit representation of set flags.
     optional int32 flags = 3;
-    repeated string tags = 4;
+    repeated string tags = 4 [ (android.privacy).dest = DEST_EXPLICIT ];
 }
 
 enum ContentType {
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 3ec6f05..8a04bf7 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -16,7 +16,6 @@
 
 syntax = "proto2";
 option java_multiple_files = true;
-option java_outer_classname = "IncidentProtoMetadata";
 
 import "frameworks/base/core/proto/android/os/batterytype.proto";
 import "frameworks/base/core/proto/android/os/cpufreq.proto";
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index d0fd0b4..43c869c 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -17,13 +17,12 @@
 syntax = "proto2";
 
 import "frameworks/base/core/proto/android/server/statlogger.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 package com.android.server;
 
 option java_multiple_files = true;
 
-import "frameworks/base/libs/incident/proto/android/privacy.proto";
-
 // Dump from com.android.server.ForceAppStandbyTracker.
 message ForceAppStandbyTrackerProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -62,6 +61,8 @@
   optional StatLoggerProto stats = 9;
 
   message ExemptedPackage {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int32 userId = 1;
     optional string package_name = 2;
   }
diff --git a/core/proto/android/server/intentresolver.proto b/core/proto/android/server/intentresolver.proto
index 60c060c..0ada895 100644
--- a/core/proto/android/server/intentresolver.proto
+++ b/core/proto/android/server/intentresolver.proto
@@ -19,9 +19,15 @@
 
 package com.android.server;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 message IntentResolverProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
 
     message ArrayMapEntry {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
         optional string key = 1;
         repeated string values = 2;
     }
diff --git a/core/proto/android/server/statlogger.proto b/core/proto/android/server/statlogger.proto
index fa430d8..2ae526a 100644
--- a/core/proto/android/server/statlogger.proto
+++ b/core/proto/android/server/statlogger.proto
@@ -20,8 +20,12 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 // Dump from StatLogger.
 message StatLoggerProto {
+  option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
   message Event {
     optional int32 eventId = 1;
     optional string label = 2;
diff --git a/core/proto/android/server/wirelesschargerdetector.proto b/core/proto/android/server/wirelesschargerdetector.proto
index 89cf2f8..2118deb 100644
--- a/core/proto/android/server/wirelesschargerdetector.proto
+++ b/core/proto/android/server/wirelesschargerdetector.proto
@@ -19,8 +19,14 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 message WirelessChargerDetectorProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     message VectorProto {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional float x = 1;
         optional float y = 2;
         optional float z = 3;
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 9013a23..5c40e5f 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -50,7 +50,7 @@
 message NotificationRecordProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional string key = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string key = 1;
 
     enum State {
         ENQUEUED = 0;
@@ -88,7 +88,7 @@
 message ManagedServicesProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional string caption = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string caption = 1;
 
     message ServiceProto {
         option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/util/event_log_tags.proto b/core/proto/android/util/event_log_tags.proto
index cb039be..457219f 100644
--- a/core/proto/android/util/event_log_tags.proto
+++ b/core/proto/android/util/event_log_tags.proto
@@ -19,17 +19,25 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 // Proto representation of event.logtags.
 // Usually sit in /system/etc/event-log-tags.
 message EventLogTagMapProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     repeated EventLogTag event_log_tags = 1;
 }
 
 message EventLogTag {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional uint32 tag_number = 1; // keyed by tag number.
     optional string tag_name = 2;
 
     message ValueDescriptor {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional string name = 1;
 
         enum DataType {
@@ -55,4 +63,4 @@
         optional DataUnit unit = 3;
     }
     repeated ValueDescriptor value_descriptors = 3;
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index ff13fab..ee258b7 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -17,11 +17,14 @@
 syntax = "proto2";
 
 import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 package android.view;
 option java_multiple_files = true;
 
 message DisplayCutoutProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
   optional .android.graphics.RectProto insets = 1;
   optional .android.graphics.RectProto bounds = 2;
 }
diff --git a/core/proto/android/view/surfacecontrol.proto b/core/proto/android/view/surfacecontrol.proto
index 9288b4f..665d688 100644
--- a/core/proto/android/view/surfacecontrol.proto
+++ b/core/proto/android/view/surfacecontrol.proto
@@ -19,10 +19,14 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 /**
  * Represents a {@link android.view.SurfaceControl} object.
  */
 message SurfaceControlProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   optional int32 hash_code = 1;
-  optional string name = 2;
+  optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a0ba3ad..d58b95a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1785,13 +1785,22 @@
 
     <!-- Must be required by an ImsService to ensure that only the
          system can bind to it.
-         <p>Protection level: signature|privileged
+         <p>Protection level: signature|privileged|vendorPrivileged
          @SystemApi
          @hide
     -->
     <permission android:name="android.permission.BIND_IMS_SERVICE"
         android:protectionLevel="signature|privileged|vendorPrivileged" />
 
+    <!-- Must be required by a DataService to ensure that only the
+         system can bind to it.
+         <p>Protection level: signature|privileged|vendorPrivileged
+         @SystemApi
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_DATA_SERVICE"
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
+
     <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
          EuiccManager APIs.
          <p>Protection level: signature|privileged|development
@@ -2723,6 +2732,15 @@
     <permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
         android:protectionLevel="signature" />
 
+   <!-- Alternative version of android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE.
+        This permission was renamed during the O previews but it was supported on the final O
+        release, so we need to carry it over.
+        <p>Protection level: signature
+        @hide
+    -->
+    <permission android:name="android.permission.BIND_AUTOFILL"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by an {@link android.service.autofill.AutofillFieldClassificationService}
          to ensure that only the system can bind to it.
          @hide This is not a third-party API (intended for OEMs and system apps).
@@ -2730,6 +2748,14 @@
     <permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be required by a android.service.textclassifier.TextClassifierService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Must be required by hotword enrollment application,
          to ensure that only the system can interact with it.
          @hide <p>Not for use by third-party applications.</p> -->
@@ -3012,6 +3038,13 @@
     <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to collect ambient light stats.
+         <p>Not for use by third party applications.</p>
+         TODO: Make a system API
+         @hide -->
+    <permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to modify the display brightness configuration
          @hide
          @SystemApi -->
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index 3b89f0d..1be915e 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -28,18 +28,18 @@
         android:layout_marginBottom="4dip"
         android:background="@drawable/list_divider_material" />
 
-    <!-- Icon will be inserted here. -->
-
-    <!-- The title and summary have some gap between them,
-    and this 'group' should be centered vertically. -->
     <LinearLayout
         android:id="@+id/content"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="?attr/dropdownListPreferredItemHeight"
         android:paddingEnd="16dip"
         android:duplicateParentState="true" >
 
-        <LinearLayout
+        <!-- Icon will be inserted here. -->
+
+        <!-- The title and summary have some gap between them,
+        and this 'group' should be centered vertically. -->
+        <RelativeLayout
             android:layout_width="0dip"
             android:layout_weight="1"
             android:layout_height="wrap_content"
@@ -71,7 +71,7 @@
                 android:duplicateParentState="true"
                 android:textAlignment="viewStart" />
 
-        </LinearLayout>
+        </RelativeLayout>
 
         <ImageView
             android:id="@+id/submenuarrow"
@@ -81,8 +81,9 @@
             android:layout_marginStart="8dp"
             android:scaleType="center"
             android:visibility="gone" />
-    </LinearLayout>
 
-    <!-- Checkbox, and/or radio button will be inserted here. -->
+        <!-- Checkbox, and/or radio button will be inserted here. -->
+
+    </LinearLayout>
 
 </com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0543305..68dad87 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3036,6 +3036,28 @@
         <!-- The title this view should present to accessibility as a pane title.
              See {@link android.view.View#setAccessibilityPaneTitle(CharSequence)} -->
         <attr name="accessibilityPaneTitle" format="string" />
+
+        <!-- Sets the color of the spot shadow that is drawn when the view has a positive Z or
+             elevation value.
+             <p>
+             By default the shadow color is black. Generally, this color will be opaque so the
+             intensity of the shadow is consistent between different views with different colors.
+             <p>
+             The opacity of the final spot shadow is a function of the shadow caster height, the
+             alpha channel of the outlineSpotShadowColor (typically opaque), and the
+             {@link android.R.attr#spotShadowAlpha} theme attribute. -->
+        <attr name="outlineSpotShadowColor" format="color" />
+
+        <!-- Sets the color of the ambient shadow that is drawn when the view has a positive Z
+             or elevation value.
+             <p>
+             By default the shadow color is black. Generally, this color will be opaque so the
+             intensity of the shadow is consistent between different views with different colors.
+             <p>
+             The opacity of the final ambient shadow is a function of the shadow caster height,
+             the alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+             {@link android.R.attr#ambientShadowAlpha} theme attribute. -->
+        <attr name="outlineAmbientShadowColor" format="color" />
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a22ca87..607414d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2477,9 +2477,9 @@
         <item>restart</item>
         <item>screenshot</item>
         <item>logout</item>
+        <item>lockdown</item>
         <item>bugreport</item>
         <item>users</item>
-        <item>lockdown</item>
     </string-array>
 
     <!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
@@ -3123,6 +3123,15 @@
     -->
     <string name="config_defaultAutofillService" translatable="false"></string>
 
+    <!-- The component name, flattened to a string, for the default system textclassifier service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         (e.g. com.android.textclassifier/.TextClassifierServiceImpl).
+         If no textclassifier service with the specified name exists on the device (or if this is
+         set to empty string), a default textclassifier will be loaded in the calling app's process.
+         See android.view.textclassifier.TextClassificationManager.
+    -->
+    <string name="config_defaultTextClassifierService" translatable="false"></string>
+
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">true</bool>
 
@@ -3266,4 +3275,6 @@
     <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string>
 
     <bool name="config_supportBluetoothPersistedState">true</bool>
+
+    <bool name="config_keepRestrictedProfilesInBackground">true</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 30586d1..dee880f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2866,6 +2866,8 @@
       <public name="lastBaselineToBottomHeight" />
       <public name="lineHeight" />
       <public name="accessibilityHeading" />
+      <public name="outlineSpotShadowColor" />
+      <public name="outlineAmbientShadowColor" />
     </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 3cde765..731b6f7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3222,19 +3222,23 @@
     <string name="dlg_ok">OK</string>
 
     <!-- USB_PREFERENCES: Notification for when the user connected to the charger only.  This is the title -->
-    <string name="usb_charging_notification_title">USB charging this device</string>
+    <string name="usb_charging_notification_title">Charging this device via USB</string>
     <!-- USB_PREFERENCES: Notification for when the user connects the phone to supply power to attached device.  This is the title -->
-    <string name="usb_supplying_notification_title">USB supplying power to attached device</string>
+    <string name="usb_supplying_notification_title">Charging connected device via USB</string>
     <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode.  This is the title -->
-    <string name="usb_mtp_notification_title">USB for file transfer</string>
+    <string name="usb_mtp_notification_title">USB file transfer turned on</string>
     <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode.  This is the title -->
-    <string name="usb_ptp_notification_title">USB for photo transfer</string>
+    <string name="usb_ptp_notification_title">PTP via USB turned on</string>
+    <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in Tethering mode.  This is the title -->
+    <string name="usb_tether_notification_title">USB tethering turned on</string>
     <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MIDI mode.  This is the title -->
-    <string name="usb_midi_notification_title">USB for MIDI</string>
+    <string name="usb_midi_notification_title">MIDI via USB turned on</string>
     <!-- USB_PREFERENCES: Notification for when a USB accessory is attached.  This is the title -->
-    <string name="usb_accessory_notification_title">Connected to a USB accessory</string>
+    <string name="usb_accessory_notification_title">USB accessory mode turned on</string>
     <!-- See USB_PREFERENCES. This is the message. -->
     <string name="usb_notification_message">Tap for more options.</string>
+    <!-- See USB_PREFERENCES. This is the message when a data mode is turned on (mtp, ptp, midi) and the device is supplying power.. -->
+    <string name="usb_power_notification_message">Charging connected device. Tap for more options.</string>
     <!-- USB_PREFERENCES: Notification for when a type-c USB audio accessory is attached but not supported.  This is the title -->
     <string name="usb_unsupported_audio_accessory_title">Analog audio accessory detected</string>
     <!-- Message of notification shown when a type-c USB audio accessory is attached but not supported. -->
@@ -4419,15 +4423,6 @@
     <!-- DO NOT TRANSLATE -->
     <string name="date_picker_day_typeface">sans-serif-medium</string>
 
-    <!-- Notify use that they are in Lock-to-app -->
-    <string name="lock_to_app_toast">To unpin this screen, touch &amp; hold Back and Overview
-        buttons</string>
-
-    <!-- Starting lock-to-app indication. -->
-    <string name="lock_to_app_start">Screen pinned</string>
-    <!-- Exting lock-to-app indication. -->
-    <string name="lock_to_app_exit">Screen unpinned</string>
-
     <!-- Lock-to-app unlock pin string -->
     <string name="lock_to_app_unlock_pin">Ask for PIN before unpinning</string>
     <!-- Lock-to-app unlock pattern string -->
@@ -4811,7 +4806,7 @@
     A toast message shown when an app shortcut that was restored from a previous device is clicked,
     but it cannot be started because the shortcut was created by a newer version of the app.
     -->
-    <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string>
+    <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string>
 
     <!--
     A toast message shown when an app shortcut that was restored from a previous device is clicked,
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 09d3121..fee5a80 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -767,9 +767,6 @@
   <java-symbol type="string" name="kilobyteShort" />
   <java-symbol type="string" name="last_month" />
   <java-symbol type="string" name="launchBrowserDefault" />
-  <java-symbol type="string" name="lock_to_app_toast" />
-  <java-symbol type="string" name="lock_to_app_start" />
-  <java-symbol type="string" name="lock_to_app_exit" />
   <java-symbol type="string" name="lock_to_app_unlock_pin" />
   <java-symbol type="string" name="lock_to_app_unlock_pattern" />
   <java-symbol type="string" name="lock_to_app_unlock_password" />
@@ -2026,8 +2023,10 @@
   <java-symbol type="string" name="usb_mtp_notification_title" />
   <java-symbol type="string" name="usb_charging_notification_title" />
   <java-symbol type="string" name="usb_notification_message" />
+  <java-symbol type="string" name="usb_power_notification_message" />
   <java-symbol type="string" name="usb_ptp_notification_title" />
   <java-symbol type="string" name="usb_midi_notification_title" />
+  <java-symbol type="string" name="usb_tether_notification_title" />
   <java-symbol type="string" name="usb_supplying_notification_title" />
   <java-symbol type="string" name="usb_unsupported_audio_accessory_title" />
   <java-symbol type="string" name="usb_unsupported_audio_accessory_message" />
@@ -3101,6 +3100,7 @@
   <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="config_defaultTextClassifierService" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
@@ -3240,4 +3240,6 @@
   <java-symbol type="string" name="slices_permission_request" />
 
   <java-symbol type="string" name="screenshot_edit" />
+
+  <java-symbol type="bool" name="config_keepRestrictedProfilesInBackground" />
 </resources>
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
new file mode 100644
index 0000000..4f7c924
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import static android.os.FileUtils.copyInternalSendfile;
+import static android.os.FileUtils.copyInternalSplice;
+import static android.os.FileUtils.copyInternalUserspace;
+
+import android.os.FileUtils.MemoryPipe;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class FileUtilsBenchmark {
+    @Param({"32", "32000", "32000000"})
+    private int mSize;
+
+    private File mSrc;
+    private File mDest;
+
+    private byte[] mData;
+
+    @BeforeExperiment
+    protected void setUp() throws Exception {
+        mSrc = new File("/data/local/tmp/src");
+        mDest = new File("/data/local/tmp/dest");
+
+        mData = new byte[mSize];
+
+        try (FileOutputStream os = new FileOutputStream(mSrc)) {
+            os.write(mData);
+        }
+    }
+
+    public void timeRegularUserspace(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (FileInputStream in = new FileInputStream(mSrc);
+                    FileOutputStream out = new FileOutputStream(mDest)) {
+                copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+
+    public void timeRegularSendfile(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (FileInputStream in = new FileInputStream(mSrc);
+                    FileOutputStream out = new FileOutputStream(mDest)) {
+                copyInternalSendfile(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+
+    public void timePipeSourceUserspace(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (MemoryPipe in = MemoryPipe.createSource(mData);
+                    FileOutputStream out = new FileOutputStream(mDest)) {
+                copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+
+    public void timePipeSourceSplice(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (MemoryPipe in = MemoryPipe.createSource(mData);
+                    FileOutputStream out = new FileOutputStream(mDest)) {
+                copyInternalSplice(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+
+    public void timePipeSinkUserspace(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (FileInputStream in = new FileInputStream(mSrc);
+                    MemoryPipe out = MemoryPipe.createSink(mData)) {
+                copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+
+    public void timePipeSinkSplice(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            try (FileInputStream in = new FileInputStream(mSrc);
+                    MemoryPipe out = MemoryPipe.createSink(mData)) {
+                copyInternalSplice(in.getFD(), out.getFD(), null, null);
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
new file mode 100644
index 0000000..f90ae34
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessDayStatsTest {
+
+    private static final LocalDate LOCAL_DATE = LocalDate.now();
+    private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100};
+    private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10};
+
+    @Test
+    public void testParamsMustNotBeNull() {
+        assertThrows(NullPointerException.class,
+                () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES));
+
+        assertThrows(NullPointerException.class,
+                () -> new AmbientBrightnessDayStats(LOCAL_DATE, null));
+
+        assertThrows(NullPointerException.class,
+                () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS));
+
+        assertThrows(NullPointerException.class,
+                () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBucketBoundariesMustNotBeEmpty() {
+        new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testStatsAndBoundariesMustHaveSameLength() {
+        float[] stats = Arrays.copyOf(STATS, STATS.length + 1);
+        stats[stats.length - 1] = 0;
+        new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats);
+    }
+
+    @Test
+    public void testAmbientBrightnessDayStatsAdd() {
+        AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES);
+        dayStats.log(0, 1);
+        dayStats.log(0.5f, 1.5f);
+        dayStats.log(50, 12.5f);
+        dayStats.log(2000, 1.24f);
+        dayStats.log(-10, 0.5f);
+        assertEquals(4, dayStats.getStats().length);
+        assertEquals(2.5f, dayStats.getStats()[0], 0);
+        assertEquals(0, dayStats.getStats()[1], 0);
+        assertEquals(12.5f, dayStats.getStats()[2], 0);
+        assertEquals(1.24f, dayStats.getStats()[3], 0);
+    }
+
+    @Test
+    public void testGetters() {
+        AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES, STATS);
+        assertEquals(LOCAL_DATE, dayStats.getLocalDate());
+        assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0);
+        assertArrayEquals(STATS, dayStats.getStats(), 0);
+    }
+
+    @Test
+    public void testParcelUnparcelAmbientBrightnessDayStats() {
+        LocalDate today = LocalDate.now();
+        AmbientBrightnessDayStats stats = new AmbientBrightnessDayStats(today,
+                new float[]{0, 1, 10, 100}, new float[]{1.3f, 2.6f, 5.8f, 10});
+        // Parcel the data
+        Parcel parcel = Parcel.obtain();
+        stats.writeToParcel(parcel, 0);
+        byte[] parceled = parcel.marshall();
+        parcel.recycle();
+        // Unparcel and check that it has not changed
+        parcel = Parcel.obtain();
+        parcel.unmarshall(parceled, 0, parceled.length);
+        parcel.setDataPosition(0);
+        AmbientBrightnessDayStats statsAgain = AmbientBrightnessDayStats.CREATOR.createFromParcel(
+                parcel);
+        assertEquals(stats, statsAgain);
+    }
+
+    @Test
+    public void testAmbientBrightnessDayStatsEquals() {
+        AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES);
+        AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]);
+        assertEquals(emptyDayStats, identicalEmptyDayStats);
+        assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode());
+
+        AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES, STATS);
+        AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES, STATS);
+        assertEquals(dayStats, identicalDayStats);
+        assertEquals(dayStats.hashCode(), identicalDayStats.hashCode());
+
+        assertNotEquals(emptyDayStats, dayStats);
+        assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode());
+
+        AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats(
+                LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS);
+        assertNotEquals(dayStats, differentDateDayStats);
+        assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode());
+
+        float[] differentStats = Arrays.copyOf(STATS, STATS.length);
+        differentStats[differentStats.length - 1] += 5f;
+        AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+                BUCKET_BOUNDARIES, differentStats);
+        assertNotEquals(dayStats, differentDateDayStats);
+        assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode());
+
+        float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES,
+                BUCKET_BOUNDARIES.length);
+        differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f;
+        AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats(
+                LOCAL_DATE, differentBucketBoundaries, STATS);
+        assertNotEquals(dayStats, differentBoundariesDayStats);
+        assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode());
+    }
+
+    private interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+
+    private static void assertThrows(Class<? extends Throwable> exceptionClass,
+            ExceptionRunnable r) {
+        try {
+            r.run();
+        } catch (Throwable e) {
+            assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
+                    + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
+            return;
+        }
+        fail("Expected exception type " + exceptionClass.getName()
+                + ", but no exception was thrown");
+    }
+
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index cd20192..b7220b3 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,16 +21,19 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.os.FileUtils.MemoryPipe;
 import android.provider.DocumentsContract.Document;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 
 import libcore.io.IoUtils;
+import libcore.io.Streams;
 
 import com.google.android.collect.Sets;
 
@@ -40,11 +43,13 @@
 import org.junit.runner.RunWith;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
 public class FileUtilsTest {
@@ -56,6 +61,8 @@
     private File mCopyFile;
     private File mTarget;
 
+    private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
+
     private Context getContext() {
         return InstrumentationRegistry.getContext();
     }
@@ -80,7 +87,7 @@
 
     @Test
     public void testCopyFile() throws Exception {
-        stageFile(mTestFile, TEST_DATA);
+        writeFile(mTestFile, TEST_DATA);
         assertFalse(mCopyFile.exists());
         FileUtils.copyFile(mTestFile, mCopyFile);
         assertTrue(mCopyFile.exists());
@@ -97,6 +104,83 @@
     }
 
     @Test
+    public void testCopy_FileToFile() throws Exception {
+        for (int size : DATA_SIZES) {
+            final File src = new File(mTarget, "src");
+            final File dest = new File(mTarget, "dest");
+
+            byte[] expected = new byte[size];
+            byte[] actual = new byte[size];
+            new Random().nextBytes(expected);
+            writeFile(src, expected);
+
+            try (FileInputStream in = new FileInputStream(src);
+                    FileOutputStream out = new FileOutputStream(dest)) {
+                FileUtils.copy(in, out);
+            }
+
+            actual = readFile(dest);
+            assertArrayEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testCopy_FileToPipe() throws Exception {
+        for (int size : DATA_SIZES) {
+            final File src = new File(mTarget, "src");
+
+            byte[] expected = new byte[size];
+            byte[] actual = new byte[size];
+            new Random().nextBytes(expected);
+            writeFile(src, expected);
+
+            try (FileInputStream in = new FileInputStream(src);
+                    MemoryPipe out = MemoryPipe.createSink(actual)) {
+                FileUtils.copy(in.getFD(), out.getFD());
+                out.join();
+            }
+
+            assertArrayEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testCopy_PipeToFile() throws Exception {
+        for (int size : DATA_SIZES) {
+            final File dest = new File(mTarget, "dest");
+
+            byte[] expected = new byte[size];
+            byte[] actual = new byte[size];
+            new Random().nextBytes(expected);
+
+            try (MemoryPipe in = MemoryPipe.createSource(expected);
+                    FileOutputStream out = new FileOutputStream(dest)) {
+                FileUtils.copy(in.getFD(), out.getFD());
+            }
+
+            actual = readFile(dest);
+            assertArrayEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testCopy_PipeToPipe() throws Exception {
+        for (int size : DATA_SIZES) {
+            byte[] expected = new byte[size];
+            byte[] actual = new byte[size];
+            new Random().nextBytes(expected);
+
+            try (MemoryPipe in = MemoryPipe.createSource(expected);
+                    MemoryPipe out = MemoryPipe.createSink(actual)) {
+                FileUtils.copy(in.getFD(), out.getFD());
+                out.join();
+            }
+
+            assertArrayEquals(expected, actual);
+        }
+    }
+
+    @Test
     public void testIsFilenameSafe() throws Exception {
         assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
         assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -106,7 +190,7 @@
 
     @Test
     public void testReadTextFile() throws Exception {
-        stageFile(mTestFile, TEST_DATA);
+        writeFile(mTestFile, TEST_DATA);
 
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
 
@@ -127,7 +211,7 @@
 
     @Test
     public void testReadTextFileWithZeroLengthFile() throws Exception {
-        stageFile(mTestFile, TEST_DATA);
+        writeFile(mTestFile, TEST_DATA);
         new FileOutputStream(mTestFile).close();  // Zero out the file
         assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
         assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -381,12 +465,21 @@
         file.setLastModified(System.currentTimeMillis() - age);
     }
 
-    private void stageFile(File file, String data) throws Exception {
-        FileWriter writer = new FileWriter(file);
-        try {
-            writer.write(data, 0, data.length());
-        } finally {
-            writer.close();
+    private void writeFile(File file, String data) throws Exception {
+        writeFile(file, data.getBytes());
+    }
+
+    private void writeFile(File file, byte[] data) throws Exception {
+        try (FileOutputStream out = new FileOutputStream(file)) {
+            out.write(data);
+        }
+    }
+
+    private byte[] readFile(File file) throws Exception {
+        try (FileInputStream in = new FileInputStream(file);
+                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            Streams.copy(in, out);
+            return out.toByteArray();
         }
     }
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 733f7a1..0083b01 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -245,6 +245,7 @@
                     Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
                     Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
                     Settings.Global.JOB_SCHEDULER_CONSTANTS,
+                    Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
                     Settings.Global.LANG_ID_UPDATE_METADATA_URL,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 8a81743..cf41eb8 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -86,15 +86,11 @@
                 .setSignature(signature)
                 .build();
 
-        // Parcel and unparcel using ParcelableWrapper.
-        final TextClassification.ParcelableWrapper parcelableReference = new TextClassification
-                .ParcelableWrapper(reference);
+        // Parcel and unparcel
         final Parcel parcel = Parcel.obtain();
-        parcelableReference.writeToParcel(parcel, parcelableReference.describeContents());
+        reference.writeToParcel(parcel, reference.describeContents());
         parcel.setDataPosition(0);
-        final TextClassification result =
-                TextClassification.ParcelableWrapper.CREATOR.createFromParcel(
-                        parcel).getTextClassification();
+        final TextClassification result = TextClassification.CREATOR.createFromParcel(parcel);
 
         assertEquals(text, result.getText());
         assertEquals(signature, result.getSignature());
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index a82542c..d6ac845 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -68,8 +68,8 @@
     public void testParcel() {
         final String fullText = "this is just a test";
         final TextLinks reference = new TextLinks.Builder(fullText)
-                .addLink(new TextLinks.TextLink(fullText, 0, 4, getEntityScores(0.f, 0.f, 1.f)))
-                .addLink(new TextLinks.TextLink(fullText, 5, 12, getEntityScores(.8f, .1f, .5f)))
+                .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+                .addLink(5, 12, getEntityScores(.8f, .1f, .5f))
                 .build();
 
         // Parcel and unparcel.
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index e920236..a6ea021 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -45,15 +45,11 @@
                 .setSignature(signature)
                 .build();
 
-        // Parcel and unparcel using ParcelableWrapper.
-        final TextSelection.ParcelableWrapper parcelableReference = new TextSelection
-                .ParcelableWrapper(reference);
+        // Parcel and unparcel
         final Parcel parcel = Parcel.obtain();
-        parcelableReference.writeToParcel(parcel, parcelableReference.describeContents());
+        reference.writeToParcel(parcel, reference.describeContents());
         parcel.setDataPosition(0);
-        final TextSelection result =
-                TextSelection.ParcelableWrapper.CREATOR.createFromParcel(
-                        parcel).getTextSelection();
+        final TextSelection result = TextSelection.CREATOR.createFromParcel(parcel);
 
         assertEquals(startIndex, result.getSelectionStartIndex());
         assertEquals(endIndex, result.getSelectionEndIndex());
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index bbca12f..69e5670 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -325,9 +325,9 @@
         TextClassificationManager textClassificationManager =
                 mActivity.getSystemService(TextClassificationManager.class);
         TextClassifier textClassifier = textClassificationManager.getTextClassifier();
-        SpannableString content = new SpannableString("Call me at +19148277737");
+        Spannable content = new SpannableString("Call me at +19148277737");
         TextLinks links = textClassifier.generateLinks(content);
-        links.apply(content, null);
+        links.apply(content, TextLinks.APPLY_STRATEGY_REPLACE, null);
 
         mActivityRule.runOnUiThread(() -> {
             textView.setText(content);
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 99bcd6c..a6c5373 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -36,10 +36,8 @@
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
index e3068920..7cd01e54 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
@@ -7,6 +7,8 @@
     <uses-sdk
         android:minSdkVersion="9"
         android:targetSdkVersion="19" />
+    <!-- Required for com.android.framework.multidexlegacytestservices.test2 -->
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
 
     <application
         android:label="MultiDexLegacyTestServices">
@@ -124,6 +126,6 @@
                 <action android:name="com.android.framework.multidexlegacytestservices.action.Service19" />
             </intent-filter>
         </service>
-        </application>
+    </application>
 
 </manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
index 7b83999..cb0a591 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
@@ -60,35 +60,40 @@
             // of the result file will be too big.
             RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
             raf.seek(raf.length());
-            Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
-            raf.writeInt(0x42434445);
+            if (raf.length() == 0) {
+                Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
+                raf.writeInt(0x42434445);
+            } else {
+                Log.w(TAG, "Service was restarted appending 0x42434445 twice at " + raf.length()
+                        + " in " + resultFile.getPath());
+                raf.writeInt(0x42434445);
+                raf.writeInt(0x42434445);
+            }
             raf.close();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        MultiDex.install(applicationContext);
-        Log.i(TAG, "Multi dex installation done.");
+            MultiDex.install(applicationContext);
+            Log.i(TAG, "Multi dex installation done.");
 
-        int value = getValue();
-        Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
-        try {
+            int value = getValue();
+            Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
             // Append the check value in result file, keeping the constant values already written.
-            RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
+            raf = new RandomAccessFile(resultFile, "rw");
             raf.seek(raf.length());
             Log.i(TAG, "Writing result at " + raf.length() + " in " + resultFile.getPath());
             raf.writeInt(value);
             raf.close();
         } catch (IOException e) {
-            e.printStackTrace();
-        }
-        try {
-            // Writing end of processing flags, the existence of the file is the criteria
-            RandomAccessFile raf = new RandomAccessFile(new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
-            Log.i(TAG, "creating complete file " + resultFile.getPath());
-            raf.writeInt(0x32333435);
-            raf.close();
-        } catch (IOException e) {
-            e.printStackTrace();
+            throw new AssertionError(e);
+        } finally {
+            try {
+                // Writing end of processing flags, the existence of the file is the criteria
+                RandomAccessFile raf = new RandomAccessFile(
+                        new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
+                Log.i(TAG, "creating complete file " + resultFile.getPath());
+                raf.writeInt(0x32333435);
+                raf.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
         }
     }
 
@@ -119,9 +124,10 @@
             intermediate = ReflectIntermediateClass.get(45, 80, 20 /* 5 seems enough on a nakasi,
                 using 20 to get some margin */);
         } catch (Exception e) {
-            e.printStackTrace();
+            throw new AssertionError(e);
         }
-        int value = new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
+        int value =
+                new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
                 new com.android.framework.multidexlegacytestservices.manymethods.Big002().get2() +
                 new com.android.framework.multidexlegacytestservices.manymethods.Big003().get3() +
                 new com.android.framework.multidexlegacytestservices.manymethods.Big004().get4() +
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
new file mode 100644
index 0000000..f3d98a8
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
+
+LOCAL_JAVA_LIBRARIES := android-support-multidex
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
new file mode 100644
index 0000000..0ab2959
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.framework.multidexlegacytestservices.test2"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="9" />
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.framework.multidexlegacytestservices" />
+
+    <application
+        android:label="multidexlegacytestservices.test2" >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
new file mode 100644
index 0000000..900f203
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.framework.multidexlegacytestservices.test2;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.concurrent.TimeoutException;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ * com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner
+ * </code>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ServicesTests {
+    private static final String TAG = "ServicesTests";
+
+    static {
+        Log.i(TAG, "Initializing");
+    }
+
+    private class ExtensionFilter implements FileFilter {
+        private final String ext;
+
+        public ExtensionFilter(String ext) {
+            this.ext = ext;
+        }
+
+        @Override
+        public boolean accept(File file) {
+            return file.getName().endsWith(ext);
+        }
+    }
+
+    private class ExtractedZipFilter extends ExtensionFilter {
+        public  ExtractedZipFilter() {
+            super(".zip");
+        }
+
+        @Override
+        public boolean accept(File file) {
+            return super.accept(file) && !file.getName().startsWith("tmp-");
+        }
+    }
+
+    private static final int ENDHDR = 22;
+
+    private static final String SERVICE_BASE_ACTION =
+            "com.android.framework.multidexlegacytestservices.action.Service";
+    private static final int MIN_SERVICE = 1;
+    private static final int MAX_SERVICE = 19;
+    private static final String COMPLETION_SUCCESS = "Success";
+
+    private File targetFilesDir;
+
+    @Before
+    public void setup() throws Exception {
+        Log.i(TAG, "setup");
+        killServices();
+
+        File applicationDataDir =
+                new File(InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir);
+        clearDirContent(applicationDataDir);
+        targetFilesDir = InstrumentationRegistry.getTargetContext().getFilesDir();
+
+        Log.i(TAG, "setup done");
+    }
+
+    @Test
+    public void testStressConcurentLaunch() throws Exception {
+        startServices();
+        waitServicesCompletion();
+        String completionStatus = getServicesCompletionStatus();
+        if (completionStatus != COMPLETION_SUCCESS) {
+            Assert.fail(completionStatus);
+        }
+    }
+
+    @Test
+    public void testRecoverFromZipCorruption() throws Exception {
+        int serviceId = 1;
+        // Ensure extraction.
+        initServicesWorkFiles();
+        startService(serviceId);
+        waitServicesCompletion(serviceId);
+
+        // Corruption of the extracted zips.
+        tamperAllExtractedZips();
+
+        killServices();
+        checkRecover();
+    }
+
+    @Test
+    public void testRecoverFromDexCorruption() throws Exception {
+        int serviceId = 1;
+        // Ensure extraction.
+        initServicesWorkFiles();
+        startService(serviceId);
+        waitServicesCompletion(serviceId);
+
+        // Corruption of the odex files.
+        tamperAllOdex();
+
+        killServices();
+        checkRecover();
+    }
+
+    @Test
+    public void testRecoverFromZipCorruptionStressTest() throws Exception {
+        Thread startServices =
+                new Thread() {
+            @Override
+            public void run() {
+                startServices();
+            }
+        };
+
+        startServices.start();
+
+        // Start services lasts more than 80s, lets cause a few corruptions during this interval.
+        for (int i = 0; i < 80; i++) {
+            Thread.sleep(1000);
+            tamperAllExtractedZips();
+        }
+        startServices.join();
+        try {
+            waitServicesCompletion();
+        } catch (TimeoutException e) {
+            // Can happen.
+        }
+
+        killServices();
+        checkRecover();
+    }
+
+    @Test
+    public void testRecoverFromDexCorruptionStressTest() throws Exception {
+        Thread startServices =
+                new Thread() {
+            @Override
+            public void run() {
+                startServices();
+            }
+        };
+
+        startServices.start();
+
+        // Start services lasts more than 80s, lets cause a few corruptions during this interval.
+        for (int i = 0; i < 80; i++) {
+            Thread.sleep(1000);
+            tamperAllOdex();
+        }
+        startServices.join();
+        try {
+            waitServicesCompletion();
+        } catch (TimeoutException e) {
+            // Will probably happen most of the time considering what we're doing...
+        }
+
+        killServices();
+        checkRecover();
+    }
+
+    private static void clearDirContent(File dir) {
+        for (File subElement : dir.listFiles()) {
+            if (subElement.isDirectory()) {
+                clearDirContent(subElement);
+            }
+            if (!subElement.delete()) {
+                throw new AssertionError("Failed to clear '" + subElement.getAbsolutePath() + "'");
+            }
+        }
+    }
+
+    private void startServices() {
+        Log.i(TAG, "start services");
+        initServicesWorkFiles();
+        for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+            startService(i);
+            try {
+                Thread.sleep((i - 1) * (1 << (i / 5)));
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    private void startService(int serviceId) {
+        Log.i(TAG, "start service " + serviceId);
+        InstrumentationRegistry.getContext().startService(new Intent(SERVICE_BASE_ACTION + serviceId));
+    }
+
+    private void initServicesWorkFiles() {
+        for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+            File resultFile = new File(targetFilesDir, "Service" + i);
+            resultFile.delete();
+            Assert.assertFalse(
+                    "Failed to delete result file '" + resultFile.getAbsolutePath() + "'.",
+                    resultFile.exists());
+            File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
+            completeFile.delete();
+            Assert.assertFalse(
+                    "Failed to delete completion file '" + completeFile.getAbsolutePath() + "'.",
+                    completeFile.exists());
+        }
+    }
+
+    private void waitServicesCompletion() throws TimeoutException {
+        Log.i(TAG, "start sleeping");
+        int attempt = 0;
+        int maxAttempt = 50; // 10 is enough for a nexus S
+        do {
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+            }
+            attempt++;
+            if (attempt >= maxAttempt) {
+                throw new TimeoutException();
+            }
+        } while (!areAllServicesCompleted());
+    }
+
+    private void waitServicesCompletion(int serviceId) throws TimeoutException {
+        Log.i(TAG, "start sleeping");
+        int attempt = 0;
+        int maxAttempt = 50; // 10 is enough for a nexus S
+        do {
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+            }
+            attempt++;
+            if (attempt >= maxAttempt) {
+                throw new TimeoutException();
+            }
+        } while (isServiceRunning(serviceId));
+    }
+
+    private String getServicesCompletionStatus() {
+        String status = COMPLETION_SUCCESS;
+        for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+            File resultFile = new File(targetFilesDir, "Service" + i);
+            if (!resultFile.isFile()) {
+                status = "Service" + i + " never completed.";
+                break;
+            }
+            if (resultFile.length() != 8) {
+                status = "Service" + i + " was restarted.";
+                break;
+            }
+        }
+        Log.i(TAG, "Services completion status: " + status);
+        return status;
+    }
+
+    private String getServiceCompletionStatus(int serviceId) {
+        String status = COMPLETION_SUCCESS;
+        File resultFile = new File(targetFilesDir, "Service" + serviceId);
+        if (!resultFile.isFile()) {
+            status = "Service" + serviceId + " never completed.";
+        } else if (resultFile.length() != 8) {
+            status = "Service" + serviceId + " was restarted.";
+        }
+        Log.i(TAG, "Service " + serviceId + " completion status: " + status);
+        return status;
+    }
+
+    private boolean areAllServicesCompleted() {
+        for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+            if (isServiceRunning(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isServiceRunning(int i) {
+        File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
+        return !completeFile.exists();
+    }
+
+    private File getSecondaryFolder() {
+        File dir =
+                new File(
+                        new File(
+                                InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir,
+                                "code_cache"),
+                        "secondary-dexes");
+        Assert.assertTrue(dir.getAbsolutePath(), dir.isDirectory());
+        return dir;
+    }
+
+    private void tamperAllExtractedZips() throws IOException {
+        // First attempt was to just overwrite zip entries but keep central directory, this was no
+        // trouble for Dalvik that was just ignoring those zip and using the odex files.
+        Log.i(TAG, "Tamper extracted zip files by overwriting all content by '\\0's.");
+        byte[] zeros = new byte[4 * 1024];
+        // Do not tamper tmp zip during their extraction.
+        for (File zip : getSecondaryFolder().listFiles(new ExtractedZipFilter())) {
+            long fileLength = zip.length();
+            Assert.assertTrue(fileLength > ENDHDR);
+            zip.setWritable(true);
+            RandomAccessFile raf = new RandomAccessFile(zip, "rw");
+            try {
+                int index = 0;
+                while (index < fileLength) {
+                    int length = (int) Math.min(zeros.length, fileLength - index);
+                    raf.write(zeros, 0, length);
+                    index += length;
+                }
+            } finally {
+                raf.close();
+            }
+        }
+    }
+
+    private void tamperAllOdex() throws IOException {
+        Log.i(TAG, "Tamper odex files by overwriting some content by '\\0's.");
+        byte[] zeros = new byte[4 * 1024];
+        // I think max size would be 40 (u1[8] + 8 u4) but it's a test so lets take big margins.
+        int savedSizeForOdexHeader = 80;
+        for (File odex : getSecondaryFolder().listFiles(new ExtensionFilter(".dex"))) {
+            long fileLength = odex.length();
+            Assert.assertTrue(fileLength > zeros.length + savedSizeForOdexHeader);
+            odex.setWritable(true);
+            RandomAccessFile raf = new RandomAccessFile(odex, "rw");
+            try {
+                raf.seek(savedSizeForOdexHeader);
+                raf.write(zeros, 0, zeros.length);
+            } finally {
+                raf.close();
+            }
+        }
+    }
+
+    private void checkRecover() throws TimeoutException {
+        Log.i(TAG, "Check recover capability");
+        int serviceId = 1;
+        // Start one service and check it was able to run correctly even if a previous run failed.
+        initServicesWorkFiles();
+        startService(serviceId);
+        waitServicesCompletion(serviceId);
+        String completionStatus = getServiceCompletionStatus(serviceId);
+        if (completionStatus != COMPLETION_SUCCESS) {
+            Assert.fail(completionStatus);
+        }
+    }
+
+    private void killServices() {
+        ((ActivityManager)
+                InstrumentationRegistry.getContext().getSystemService(Context.ACTIVITY_SERVICE))
+        .killBackgroundProcesses("com.android.framework.multidexlegacytestservices");
+    }
+}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dad24da..22867df 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -333,6 +333,9 @@
     <family lang="und-Cari">
         <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
     </family>
+    <family lang="und-Cakm">
+        <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+    </family>
     <family lang="und-Cher">
         <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
     </family>
@@ -429,6 +432,9 @@
     <family lang="und-Orkh">
         <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
     </family>
+    <family lang="und-Osge">
+        <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+    </family>
     <family lang="und-Osma">
         <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
     </family>
diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png
new file mode 100644
index 0000000..edf926d
--- /dev/null
+++ b/docs/html/reference/images/text/style/drawablemarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png
new file mode 100644
index 0000000..8776b03
--- /dev/null
+++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png
new file mode 100644
index 0000000..8ec39be
--- /dev/null
+++ b/docs/html/reference/images/text/style/iconmarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png
new file mode 100644
index 0000000..c03e6bb
--- /dev/null
+++ b/docs/html/reference/images/text/style/imagespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png
new file mode 100644
index 0000000..6e55dbc
--- /dev/null
+++ b/docs/html/reference/images/text/style/maskfilterspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png
new file mode 100644
index 0000000..9ffa05b
--- /dev/null
+++ b/docs/html/reference/images/text/style/stylespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png
new file mode 100644
index 0000000..89a1121
--- /dev/null
+++ b/docs/html/reference/images/text/style/tabstopspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png
new file mode 100644
index 0000000..67e2cf9
--- /dev/null
+++ b/docs/html/reference/images/text/style/typefacespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png
new file mode 100644
index 0000000..1134520
--- /dev/null
+++ b/docs/html/reference/images/text/style/urlspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 627d551..69a5874 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -541,10 +541,19 @@
         return mAllowHwBitmapsInSwMode;
     }
 
+    /**
+     * @hide
+     */
+    protected void onHwBitmapInSwMode() {
+        if (!mAllowHwBitmapsInSwMode) {
+            throw new IllegalArgumentException(
+                    "Software rendering doesn't support hardware bitmaps");
+        }
+    }
+
     private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
-        if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
-                && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
-            throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+            onHwBitmapInSwMode();
         }
     }
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0072012..44e7066 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
 import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.io.OutputStream;
@@ -1171,6 +1175,82 @@
     }
 
     /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+     * width and height the same as the Picture's width and height and a Config.HARDWARE
+     * config.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+        return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+    }
+
+    /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * The bitmap will be immutable with the given width and height. If the width and height
+     * are not the same as the Picture's width & height, the Picture will be scaled to
+     * fit the given width and height.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @param width The width of the bitmap to create. The picture's width will be
+     *              scaled to match if necessary.
+     * @param height The height of the bitmap to create. The picture's height will be
+     *              scaled to match if necessary.
+     * @param config The {@link Config} of the created bitmap. If this is null then
+     *               the bitmap will be {@link Config#HARDWARE}.
+     *
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+            @NonNull Config config) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width & height must be > 0");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("Config must not be null");
+        }
+        if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+            StrictMode.noteSlowCall("GPU readback");
+        }
+        if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+            final RenderNode node = RenderNode.create("BitmapTemporary", null);
+            node.setLeftTopRightBottom(0, 0, width, height);
+            node.setClipToBounds(false);
+            final DisplayListCanvas canvas = node.start(width, height);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            node.end(canvas);
+            Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+            if (config != Config.HARDWARE) {
+                bitmap = bitmap.copy(config, false);
+            }
+            return bitmap;
+        } else {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+            Canvas canvas = new Canvas(bitmap);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            canvas.setBitmap(null);
+            bitmap.makeImmutable();
+            return bitmap;
+        }
+    }
+
+    /**
      * Returns an optional array of private data, used by the UI system for
      * some bitmaps. Not intended to be called by applications.
      */
@@ -1259,6 +1339,12 @@
         return mIsMutable;
     }
 
+    /** @hide */
+    public final void makeImmutable() {
+        // todo mIsMutable = false;
+        // todo nMakeImmutable();
+    }
+
     /**
      * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
      * When a pixel is pre-multiplied, the RGB components have been multiplied by
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d77e601..fe2b523 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -160,25 +160,6 @@
                 isItalic);
     }
 
-    /**
-     * Allow creating unsupported FontFamily.
-     *
-     * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
-     * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
-     * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
-     * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
-     * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
-     * the top of the fallback chain but is never used. if we don't create this empty FontFamily
-     * and put it at top, bad things (performance regressions, unexpected glyph selection) will
-     * happen.
-     */
-    public void allowUnsupportedFont() {
-        if (mBuilderPtr == 0) {
-            throw new IllegalStateException("Unable to allow unsupported font.");
-        }
-        nAllowUnsupportedFont(mBuilderPtr);
-    }
-
     // TODO: Remove once internal user stop using private API.
     private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
         return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,9 +171,6 @@
     private static native long nCreateFamily(long mBuilderPtr);
 
     @CriticalNative
-    private static native void nAllowUnsupportedFont(long builderPtr);
-
-    @CriticalNative
     private static native void nAbort(long mBuilderPtr);
 
     @CriticalNative
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 08eeaff..9ac94d8 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,8 +31,9 @@
  * be replayed on a hardware accelerated canvas.</p>
  */
 public class Picture {
-    private Canvas mRecordingCanvas;
+    private PictureCanvas mRecordingCanvas;
     private long mNativePicture;
+    private boolean mRequiresHwAcceleration;
 
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
@@ -78,8 +79,12 @@
      * into it.
      */
     public Canvas beginRecording(int width, int height) {
+        if (mRecordingCanvas != null) {
+            throw new IllegalStateException("Picture already recording, must call #endRecording()");
+        }
         long ni = nativeBeginRecording(mNativePicture, width, height);
-        mRecordingCanvas = new RecordingCanvas(this, ni);
+        mRecordingCanvas = new PictureCanvas(this, ni);
+        mRequiresHwAcceleration = false;
         return mRecordingCanvas;
     }
 
@@ -91,6 +96,7 @@
      */
     public void endRecording() {
         if (mRecordingCanvas != null) {
+            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
             mRecordingCanvas = null;
             nativeEndRecording(mNativePicture);
         }
@@ -113,6 +119,18 @@
     }
 
     /**
+     * Indicates whether or not this Picture contains recorded commands that only work when
+     * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+     * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+     *
+     * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+     *         false otherwise.
+     */
+    public boolean requiresHardwareAcceleration() {
+        return mRequiresHwAcceleration;
+    }
+
+    /**
      * Draw this picture on the canvas.
      * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
+        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+            canvas.onHwBitmapInSwMode();
+        }
         nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
 
@@ -164,8 +185,7 @@
         if (stream == null) {
             throw new NullPointerException();
         }
-        if (!nativeWriteToStream(mNativePicture, stream,
-                             new byte[WORKING_STREAM_STORAGE])) {
+        if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
             throw new RuntimeException();
         }
     }
@@ -182,10 +202,11 @@
                                            OutputStream stream, byte[] storage);
     private static native void nativeDestructor(long nativePicture);
 
-    private static class RecordingCanvas extends Canvas {
+    private static class PictureCanvas extends Canvas {
         private final Picture mPicture;
+        boolean mHoldsHwBitmap;
 
-        public RecordingCanvas(Picture pict, long nativeCanvas) {
+        public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
             mPicture = pict;
         }
@@ -202,5 +223,10 @@
             }
             super.drawPicture(picture);
         }
+
+        @Override
+        protected void onHwBitmapInSwMode() {
+            mHoldsHwBitmap = true;
+        }
     }
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ef41507..04c5295 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -818,12 +818,9 @@
             if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
                     0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
                     null /* axes */)) {
-                // Due to backward compatibility, even if the font is not supported by our font
-                // stack, we need to place the empty font at the first place. The typeface with
-                // empty font behaves different from default typeface especially in fallback
-                // font selection.
-                fontFamily.allowUnsupportedFont();
-                fontFamily.freeze();
+                if (!fontFamily.freeze()) {
+                    return Typeface.DEFAULT;
+                }
                 final FontFamily[] families = { fontFamily };
                 typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
                         RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
@@ -870,12 +867,9 @@
         final FontFamily fontFamily = new FontFamily();
         if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
                   RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
-            // Due to backward compatibility, even if the font is not supported by our font
-            // stack, we need to place the empty font at the first place. The typeface with
-            // empty font behaves different from default typeface especially in fallback font
-            // selection.
-            fontFamily.allowUnsupportedFont();
-            fontFamily.freeze();
+            if (!fontFamily.freeze()) {
+                return Typeface.DEFAULT;
+            }
             FontFamily[] families = { fontFamily };
             return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
                     RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 4328109..27c8fda0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -319,7 +319,8 @@
     /**
      *  Start the animation.
      *
-     *  <p>Does nothing if the animation is already running.
+     *  <p>Does nothing if the animation is already running. If the animation is stopped,
+     *  this will reset it.</p>
      *
      *  <p>If the animation starts, this will call
      *  {@link Animatable2.AnimationCallback#onAnimationStart}.</p>
@@ -347,7 +348,9 @@
         if (mState == null) {
             throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
         }
-        nStop(mState.mNativePtr);
+        if (nStop(mState.mNativePtr)) {
+            postOnAnimationEnd();
+        }
     }
 
     // Animatable2 overrides
@@ -364,21 +367,31 @@
             nSetOnAnimationEndListener(mState.mNativePtr, this);
         }
 
-        mAnimationCallbacks.add(callback);
+        if (!mAnimationCallbacks.contains(callback)) {
+            mAnimationCallbacks.add(callback);
+        }
     }
 
     @Override
     public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
-        if (callback == null || mAnimationCallbacks == null) {
+        if (callback == null || mAnimationCallbacks == null
+                || !mAnimationCallbacks.remove(callback)) {
             return false;
         }
 
-        return mAnimationCallbacks.remove(callback);
+        if (mAnimationCallbacks.isEmpty()) {
+            clearAnimationCallbacks();
+        }
+
+        return true;
     }
 
     @Override
     public void clearAnimationCallbacks() {
-        mAnimationCallbacks = null;
+        if (mAnimationCallbacks != null) {
+            mAnimationCallbacks = null;
+            nSetOnAnimationEndListener(mState.mNativePtr, null);
+        }
     }
 
     private void postOnAnimationStart() {
@@ -412,6 +425,21 @@
         return mHandler;
     }
 
+    /**
+     *  Called by JNI.
+     *
+     *  The JNI code has already posted this to the thread that created the
+     *  callback, so no need to post.
+     */
+    @SuppressWarnings("unused")
+    private void onAnimationEnd() {
+        if (mAnimationCallbacks != null) {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationEnd(this);
+            }
+        }
+    }
+
 
     private static native long nCreate(long nativeImageDecoder,
             @Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
@@ -431,7 +459,7 @@
     @FastNative
     private static native boolean nStart(long nativePtr);
     @FastNative
-    private static native void nStop(long nativePtr);
+    private static native boolean nStop(long nativePtr);
     @FastNative
     private static native void nSetLoopCount(long nativePtr, int loopCount);
     // Pass the drawable down to native so it can call onAnimationEnd.
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index f721ed3..09b3b9b 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -248,7 +248,8 @@
                         spec.getUserAuthenticationValidityDurationSeconds(),
                         spec.isUserAuthenticationValidWhileOnBody(),
                         spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                        spec.isUserConfirmationRequired());
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -289,7 +290,8 @@
                 spec.getUserAuthenticationValidityDurationSeconds(),
                 spec.isUserAuthenticationValidWhileOnBody(),
                 spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                spec.isUserConfirmationRequired());
         if (spec.isTrustedUserPresenceRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
         }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d1eb688..e33e3cd 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -349,7 +349,8 @@
                         mSpec.getUserAuthenticationValidityDurationSeconds(),
                         mSpec.isUserAuthenticationValidWhileOnBody(),
                         mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                        mSpec.isUserConfirmationRequired());
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -545,7 +546,8 @@
                 mSpec.getUserAuthenticationValidityDurationSeconds(),
                 mSpec.isUserAuthenticationValidWhileOnBody(),
                 mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                mSpec.isUserConfirmationRequired());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 9df37f5..7bbc099 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -190,6 +190,8 @@
                     && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
         }
 
+        boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+
         return new KeyInfo(entryAlias,
                 insideSecureHardware,
                 origin,
@@ -207,7 +209,8 @@
                 userAuthenticationRequirementEnforcedBySecureHardware,
                 userAuthenticationValidWhileOnBody,
                 trustedUserPresenceRequred,
-                invalidatedByBiometricEnrollment);
+                invalidatedByBiometricEnrollment,
+                userConfirmationRequired);
     }
 
     private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 440e086..05cc74a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -502,7 +502,8 @@
                         spec.getUserAuthenticationValidityDurationSeconds(),
                         spec.isUserAuthenticationValidWhileOnBody(),
                         spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId());
+                        spec.getBoundToSpecificSecureUserId(),
+                        spec.isUserConfirmationRequired());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -704,7 +705,8 @@
                     params.getUserAuthenticationValidityDurationSeconds(),
                     params.isUserAuthenticationValidWhileOnBody(),
                     params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId());
+                    params.getBoundToSpecificSecureUserId(),
+                    params.isUserConfirmationRequired());
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a896c72..da23c70 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -264,6 +264,7 @@
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
+    private final boolean mUserConfirmationRequired;
 
     /**
      * @hide should be built with Builder
@@ -293,7 +294,8 @@
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
-            boolean isStrongBoxBacked) {
+            boolean isStrongBoxBacked,
+            boolean userConfirmationRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -341,6 +343,7 @@
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -547,6 +550,26 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link Builder#setUserAuthenticationRequired(boolean)}). Keys can be created that require
+     * confirmation but not user authentication, or user authentication but not confirmation, or
+     * both. Confirmation verifies that some user with physical possession of the device has
+     * approved a displayed message. User authentication verifies that the correct user is present
+     * and has authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
@@ -675,6 +698,7 @@
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
+        private boolean mUserConfirmationRequired;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1063,6 +1087,29 @@
         }
 
         /**
+         * Sets whether this key is authorized to be used only for messages confirmed by the
+         * user.
+         *
+         * Confirmation is separate from user authentication (see
+         * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+         * confirmation but not user authentication, or user authentication but not confirmation,
+         * or both. Confirmation verifies that some user with physical possession of the device has
+         * approved a displayed message. User authentication verifies that the correct user is
+         * present and has authenticated.
+         *
+         * <p>This authorization applies only to secret key and private key operations. Public key
+         * operations are not restricted.
+         *
+         * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+         * more details about user confirmations.
+         */
+        @NonNull
+        public Builder setUserConfirmationRequired(boolean required) {
+            mUserConfirmationRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the duration of time (seconds) for which this key is authorized to be used after the
          * user is successfully authenticated. This has effect if the key requires user
          * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -1249,7 +1296,8 @@
                     mUniqueIdIncluded,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
-                    mIsStrongBoxBacked);
+                    mIsStrongBoxBacked,
+                    mUserConfirmationRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 864f62a..0a75cd5 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -82,6 +82,7 @@
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mTrustedUserPresenceRequired;
     private final boolean mInvalidatedByBiometricEnrollment;
+    private final boolean mUserConfirmationRequired;
 
     /**
      * @hide
@@ -103,7 +104,8 @@
             boolean userAuthenticationRequirementEnforcedBySecureHardware,
             boolean userAuthenticationValidWhileOnBody,
             boolean trustedUserPresenceRequired,
-            boolean invalidatedByBiometricEnrollment) {
+            boolean invalidatedByBiometricEnrollment,
+            boolean userConfirmationRequired) {
         mKeystoreAlias = keystoreKeyAlias;
         mInsideSecureHardware = insideSecureHardware;
         mOrigin = origin;
@@ -125,6 +127,7 @@
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mTrustedUserPresenceRequired = trustedUserPresenceRequired;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -260,6 +263,27 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+     * not user authentication, or user authentication but not confirmation, or both. Confirmation
+     * verifies that some user with physical possession of the device has approved a displayed
+     * message. User authentication verifies that the correct user is present and has
+     * authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see KeyGenParameterSpec.Builder#setUserConfirmationRequired(boolean)
+     * @see KeyProtection.Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index dbacb9c..b5b3281 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -228,6 +228,7 @@
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
+    private final boolean mUserConfirmationRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -244,7 +245,8 @@
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
-            boolean criticalToDeviceEncryption) {
+            boolean criticalToDeviceEncryption,
+            boolean userConfirmationRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -262,6 +264,7 @@
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -396,6 +399,26 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+     * not user authentication, or user authentication but not confirmation, or both. Confirmation
+     * verifies that some user with physical possession of the device has approved a displayed
+     * message. User authentication verifies that the correct user is present and has
+     * authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
@@ -488,6 +511,7 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
+        private boolean mUserConfirmationRequired;
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -719,6 +743,29 @@
         }
 
         /**
+         * Sets whether this key is authorized to be used only for messages confirmed by the
+         * user.
+         *
+         * Confirmation is separate from user authentication (see
+         * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+         * confirmation but not user authentication, or user authentication but not confirmation,
+         * or both. Confirmation verifies that some user with physical possession of the device has
+         * approved a displayed message. User authentication verifies that the correct user is
+         * present and has authenticated.
+         *
+         * <p>This authorization applies only to secret key and private key operations. Public key
+         * operations are not restricted.
+         *
+         * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+         * more details about user confirmations.
+         */
+        @NonNull
+        public Builder setUserConfirmationRequired(boolean required) {
+            mUserConfirmationRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the duration of time (seconds) for which this key is authorized to be used after the
          * user is successfully authenticated. This has effect if the key requires user
          * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -866,7 +913,8 @@
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
-                    mCriticalToDeviceEncryption);
+                    mCriticalToDeviceEncryption,
+                    mUserConfirmationRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 34c8d1f..4e28601 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -16,6 +16,7 @@
 
 package android.security.keystore;
 
+import android.util.Log;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
 import android.security.KeyStore;
@@ -93,6 +94,8 @@
      *        overriding the default logic in this method where the key is bound to either the root
      *        SID of the current user, or the fingerprint SID if explicit fingerprint authorization
      *        is requested.
+     * @param userConfirmationRequired whether user confirmation is required to authorize the use
+     *        of the key.
      * @throws IllegalStateException if user authentication is required but the system is in a wrong
      *         state (e.g., secure lock screen not set up) for generating or importing keys that
      *         require user authentication.
@@ -102,7 +105,12 @@
             int userAuthenticationValidityDurationSeconds,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId) {
+            long boundToSpecificSecureUserId,
+            boolean userConfirmationRequired) {
+        if (userConfirmationRequired) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+        }
+
         if (!userAuthenticationRequired) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 3d2c252..55f4d89 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -507,12 +507,20 @@
                getOutline().getAlpha() != 0.0f;
     }
 
-    SkColor getShadowColor() const {
-        return mPrimitiveFields.mShadowColor;
+    SkColor getSpotShadowColor() const {
+        return mPrimitiveFields.mSpotShadowColor;
     }
 
-    bool setShadowColor(SkColor shadowColor) {
-        return RP_SET(mPrimitiveFields.mShadowColor, shadowColor);
+    bool setSpotShadowColor(SkColor shadowColor) {
+        return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
+    }
+
+    SkColor getAmbientShadowColor() const {
+        return mPrimitiveFields.mAmbientShadowColor;
+    }
+
+    bool setAmbientShadowColor(SkColor shadowColor) {
+        return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
     }
 
     bool fitsOnLayer() const {
@@ -538,7 +546,8 @@
         int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
         int mWidth = 0, mHeight = 0;
         int mClippingFlags = CLIP_TO_BOUNDS;
-        SkColor mShadowColor = SK_ColorBLACK;
+        SkColor mSpotShadowColor = SK_ColorBLACK;
+        SkColor mAmbientShadowColor = SK_ColorBLACK;
         float mAlpha = 1;
         float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
         float mElevation = 0;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 264b95e..2bded9b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -41,12 +41,17 @@
         return false;
     }
 
+    // This will trigger a reset.
+    mFinished = true;
+
     mRunning = true;
     return true;
 }
 
-void AnimatedImageDrawable::stop() {
+bool AnimatedImageDrawable::stop() {
+    bool wasRunning = mRunning;
     mRunning = false;
+    return wasRunning;
 }
 
 bool AnimatedImageDrawable::isRunning() {
@@ -177,7 +182,6 @@
     if (finalFrame) {
         if (mEndListener) {
             mEndListener->onAnimationEnd();
-            mEndListener = nullptr;
         }
     }
 }
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 9d84ed5..2fd6f40 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -68,7 +68,9 @@
     // Returns true if the animation was started; false otherwise (e.g. it was
     // already running)
     bool start();
-    void stop();
+    // Returns true if the animation was stopped; false otherwise (e.g. it was
+    // already stopped)
+    bool stop();
     bool isRunning();
     void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
 
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 43f46ef..a0d000d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -44,7 +44,6 @@
     minikinPaint.familyVariant = paint->getFamilyVariant();
     minikinPaint.fontStyle = resolvedFace->fStyle;
     minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
-    minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
     return minikinPaint;
 }
 
@@ -55,18 +54,23 @@
     minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
     minikin::Layout layout;
 
+    const minikin::U16StringPiece textBuf(buf, bufSize);
+    const minikin::Range range(start, start + count);
+    const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+    const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+    const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
     if (mt == nullptr) {
-        layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+        layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
         return layout;
     }
 
-    if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
-                        minikin::Range(start, start + count),
-                        minikinPaint, bidiFlags, mtOffset, &layout)) {
+    if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen,
+                        &layout)) {
         return layout;
     }
 
-    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+    layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen);
     return layout;
 }
 
@@ -74,8 +78,14 @@
                                 const Typeface* typeface, const uint16_t* buf, size_t start,
                                 size_t count, size_t bufSize, float* advances) {
     minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
-    return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
-                                        advances, nullptr /* extent */);
+    const minikin::U16StringPiece textBuf(buf, bufSize);
+    const minikin::Range range(start, start + count);
+    const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+    const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+    const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
+    return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
+                                        endHyphen, advances, nullptr /* extent */);
 }
 
 bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index ebc14c8..091b526 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -182,10 +182,11 @@
 
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
             std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
-    std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
-            std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
-    std::shared_ptr<minikin::FontCollection> collection =
-            std::make_shared<minikin::FontCollection>(std::move(family));
+    std::vector<minikin::Font> fonts;
+    fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+
+    std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
+            std::make_shared<minikin::FontFamily>(std::move(fonts)));
 
     Typeface* hwTypeface = new Typeface();
     hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 7b59ccf..25c51f2 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -111,6 +111,10 @@
     }
 }
 
+static SkColor multiplyAlpha(SkColor color, float alpha) {
+    return SkColorSetA(color, alpha * SkColorGetA(color));
+}
+
 // copied from FrameBuilder::deferShadow
 void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
     const RenderProperties& casterProperties = caster->getNodeProperties();
@@ -187,9 +191,11 @@
     } else {
         zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
     }
+    SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha);
+    SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha);
     SkShadowUtils::DrawShadow(
             canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(),
-            ambientAlpha, spotAlpha, casterProperties.getShadowColor(),
+            ambientColor, spotColor,
             casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
 }
 
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 66d6f52..2232c25 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -56,8 +56,9 @@
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
             std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
-    return std::make_shared<minikin::FontFamily>(
-            std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
+    std::vector<minikin::Font> fonts;
+    fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+    return std::make_shared<minikin::FontFamily>(std::move(fonts));
 }
 
 std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 018db9a..fa3f99a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -88,11 +88,6 @@
     boolean providerMeetsCriteria(String provider, in Criteria criteria);
     ProviderProperties getProviderProperties(String provider);
     String getNetworkProviderPackage();
-    boolean isProviderEnabled(String provider);
-    boolean isProviderEnabledForUser(String provider, int userId);
-    boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
-    boolean isLocationEnabledForUser(int userId);
-    void setLocationEnabledForUser(boolean enabled, int userId);
 
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 603926f..98e67c2 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -17,7 +17,9 @@
 package com.android.internal.location.gnssmetrics;
 
 import android.os.SystemClock;
+import android.os.connectivity.GpsBatteryStats;
 
+import android.text.format.DateUtils;
 import android.util.Base64;
 import android.util.Log;
 import android.util.TimeUtils;
@@ -26,6 +28,7 @@
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.nano.GnssLogsProto.GnssLog;
+import com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
 
 /**
  * GnssMetrics: Is used for logging GNSS metrics
@@ -171,6 +174,7 @@
       msg.standardDeviationTopFourAverageCn0DbHz
           = topFourAverageCn0Statistics.getStandardDeviation();
     }
+    msg.powerMetrics = mGnssPowerMetrics.buildProto();
     String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT);
     reset();
     return s;
@@ -218,6 +222,21 @@
           topFourAverageCn0Statistics.getStandardDeviation()).append("\n");
     }
     s.append("GNSS_KPI_END").append("\n");
+    GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
+    if (stats != null) {
+      s.append("Power Metrics").append('\n');
+      long[] t = stats.getTimeInGpsSignalQualityLevel();
+      if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+        s.append("  Amount of time (while on battery) Top 4 Avg CN0 > " +
+            Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) +
+            " dB-Hz (min): ").append(t[1] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
+        s.append("  Amount of time (while on battery) Top 4 Avg CN0 <= " +
+            Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) +
+            " dB-Hz (min): ").append(t[0] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
+      }
+      s.append("  Energy consumed while on battery (mAh): ").append(
+          stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS)).append("\n");
+    }
     return s.toString();
   }
 
@@ -294,7 +313,7 @@
   private class GnssPowerMetrics {
 
     /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */
-    private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
+    public static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
 
     /* Minimum change in Top Four Average CN0 needed to trigger a report */
     private static final double REPORTING_THRESHOLD_DB_HZ = 1.0;
@@ -313,6 +332,38 @@
     }
 
     /**
+     * Builds power metrics proto buf. This is included in the gnss proto buf.
+     * @return PowerMetrics
+     */
+    public PowerMetrics buildProto() {
+      PowerMetrics p = new PowerMetrics();
+      GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
+      if (stats != null) {
+        p.loggingDurationMs = stats.getLoggingDurationMs();
+        p.energyConsumedMah = stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS);
+        long[] t = stats.getTimeInGpsSignalQualityLevel();
+        p.timeInSignalQualityLevelMs = new long[t.length];
+        for (int i = 0; i < t.length; i++) {
+          p.timeInSignalQualityLevelMs[i] = t[i];
+        }
+      }
+      return p;
+    }
+
+    /**
+     * Returns the GPS power stats
+     * @return GpsBatteryStats
+     */
+    public GpsBatteryStats getGpsBatteryStats() {
+      try {
+        return mBatteryStats.getGpsBatteryStats();
+      } catch (Exception e) {
+        Log.w(TAG, "Exception", e);
+        return null;
+      }
+    }
+
+    /**
      * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If
      * the number of SVs seen is less than 4, then signal quality is the average CN0.
      * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ.
@@ -347,4 +398,4 @@
       return GnssMetrics.GPS_SIGNAL_QUALITY_POOR;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 41f9f09..3d879f5 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -22,6 +22,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.TreeSet;
 
 /**
@@ -176,6 +177,19 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AudioDeviceInfo that = (AudioDeviceInfo) o;
+        return Objects.equals(getPort(), that.getPort());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getPort());
+    }
+
     private final AudioDevicePort mPort;
 
     AudioDeviceInfo(AudioDevicePort port) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d042..f98480b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -265,6 +265,12 @@
     public static final int ENCODING_AAC_XHE = 16;
     /** Audio data format: AC-4 sync frame transport format */
     public static final int ENCODING_AC4 = 17;
+    /** Audio data format: E-AC-3-JOC compressed
+     * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+     * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+     * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+     **/
+    public static final int ENCODING_E_AC3_JOC = 18;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@
         case ENCODING_PCM_FLOAT:
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -537,6 +544,7 @@
         case ENCODING_PCM_FLOAT:
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_IEC61937:
@@ -564,6 +572,7 @@
             return true;
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -593,6 +602,7 @@
             return true;
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -829,6 +839,7 @@
                 case ENCODING_PCM_FLOAT:
                 case ENCODING_AC3:
                 case ENCODING_E_AC3:
+                case ENCODING_E_AC3_JOC:
                 case ENCODING_DTS:
                 case ENCODING_DTS_HD:
                 case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@
         ENCODING_PCM_FLOAT,
         ENCODING_AC3,
         ENCODING_E_AC3,
+        ENCODING_E_AC3_JOC,
         ENCODING_DTS,
         ENCODING_DTS_HD,
         ENCODING_IEC61937,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 22fa620..bf51d97 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -48,11 +48,13 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.view.KeyEvent;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -4578,6 +4580,51 @@
         }
     }
 
+    /**
+     * Set port id for microphones by matching device type and address.
+     * @hide
+     */
+    public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) {
+        AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
+        for (int i = microphones.size() - 1; i >= 0; i--) {
+            boolean foundPortId = false;
+            for (AudioDeviceInfo device : devices) {
+                if (device.getPort().type() == microphones.get(i).getInternalDeviceType()
+                        && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) {
+                    microphones.get(i).setId(device.getId());
+                    foundPortId = true;
+                    break;
+                }
+            }
+            if (!foundPortId) {
+                Log.i(TAG, "Failed to find port id for device with type:"
+                        + microphones.get(i).getType() + " address:"
+                        + microphones.get(i).getAddress());
+                microphones.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
+     * of all available microphones. The list is empty when no microphones are available
+     * on the device. An error during the query will result in an IOException being thrown.
+     *
+     * @return a list that contains all microphones' characteristics
+     * @throws IOException if an error occurs.
+     */
+    public List<MicrophoneInfo> getMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
+        int status = AudioSystem.getMicrophones(microphones);
+        if (status != AudioManager.SUCCESS) {
+            // fail and bail!
+            Log.e(TAG, "getMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>(); // Always return a list.
+        }
+        setPortIdForMicrophones(microphones);
+        return microphones;
+    }
+
     // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
     // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
     // of the ports that exist at the time of the last notification.
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
new file mode 100644
index 0000000..4652c18
--- /dev/null
+++ b/media/java/android/media/AudioPresentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * The AudioPresentation class encapsulates the information that describes an audio presentation
+ * which is available in next generation audio content.
+ *
+ * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
+ * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
+ * presentations and to select one.
+ *
+ * A list of available audio presentations in a media source can be queried using
+ * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
+ * selection.
+ * An AudioPresentation can be passed to an offloaded audio decoder via
+ * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
+ * presentation. An audio stream may contain multiple presentations that differ by language,
+ * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
+ * a set of description labels in different languages to help the user to make an informed
+ * selection.
+ */
+public final class AudioPresentation {
+    private final int mPresentationId;
+    private final int mProgramId;
+    private final Map<String, String> mLabels;
+    private final String mLanguage;
+
+    /** @hide */
+    @IntDef(
+        value = {
+            MASTERING_NOT_INDICATED,
+            MASTERED_FOR_STEREO,
+            MASTERED_FOR_SURROUND,
+            MASTERED_FOR_3D,
+            MASTERED_FOR_HEADPHONE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MasteringIndicationType {}
+
+    private final @MasteringIndicationType int mMasteringIndication;
+    private final boolean mAudioDescriptionAvailable;
+    private final boolean mSpokenSubtitlesAvailable;
+    private final boolean mDialogueEnhancementAvailable;
+
+    /**
+     * No preferred reproduction channel layout.
+     */
+    public static final int MASTERING_NOT_INDICATED         = 0;
+    /**
+     * Stereo speaker layout.
+     */
+    public static final int MASTERED_FOR_STEREO             = 1;
+    /**
+     * Two-dimensional (e.g. 5.1) speaker layout.
+     */
+    public static final int MASTERED_FOR_SURROUND           = 2;
+    /**
+     * Three-dimensional (e.g. 5.1.2) speaker layout.
+     */
+    public static final int MASTERED_FOR_3D                 = 3;
+    /**
+     * Prerendered for headphone playback.
+     */
+    public static final int MASTERED_FOR_HEADPHONE          = 4;
+
+    AudioPresentation(int presentationId,
+                        int programId,
+                        Map<String, String> labels,
+                        String language,
+                        @MasteringIndicationType int masteringIndication,
+                        boolean audioDescriptionAvailable,
+                        boolean spokenSubtitlesAvailable,
+                        boolean dialogueEnhancementAvailable) {
+        this.mPresentationId = presentationId;
+        this.mProgramId = programId;
+        this.mLanguage = language;
+        this.mMasteringIndication = masteringIndication;
+        this.mAudioDescriptionAvailable = audioDescriptionAvailable;
+        this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+        this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+
+        this.mLabels = new HashMap<String, String>(labels);
+    }
+
+    /**
+     * The framework uses this presentation id to select an audio presentation rendered by a
+     * decoder. Presentation id is typically sequential, but does not have to be.
+     * @hide
+     */
+    public int getPresentationId() {
+        return mPresentationId;
+    }
+
+    /**
+     * The framework uses this program id to select an audio presentation rendered by a decoder.
+     * Program id can be used to further uniquely identify the presentation to a decoder.
+     * @hide
+     */
+    public int getProgramId() {
+        return mProgramId;
+    }
+
+    /**
+     * @return a map of available text labels for this presentation. Each label is indexed by its
+     * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B
+     * or ISO 639-2/T could be used.
+     */
+    public Map<Locale, String> getLabels() {
+        Map<Locale, String> localeLabels = new HashMap<>();
+        for (Map.Entry<String, String> entry : mLabels.entrySet()) {
+            localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+        }
+        return localeLabels;
+    }
+
+    /**
+     * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
+     */
+    public Locale getLocale() {
+        return new Locale(mLanguage);
+    }
+
+    /**
+     * @return the mastering indication of the audio presentation.
+     * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
+     * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+     */
+    @MasteringIndicationType
+    public int getMasteringIndication() {
+        return mMasteringIndication;
+    }
+
+    /**
+     * Indicates whether an audio description for the visually impaired is available.
+     * @return {@code true} if audio description is available.
+     */
+    public boolean hasAudioDescription() {
+        return mAudioDescriptionAvailable;
+    }
+
+    /**
+     * Indicates whether spoken subtitles for the visually impaired are available.
+     * @return {@code true} if spoken subtitles are available.
+     */
+    public boolean hasSpokenSubtitles() {
+        return mSpokenSubtitlesAvailable;
+    }
+
+    /**
+     * Indicates whether dialogue enhancement is available.
+     * @return {@code true} if dialogue enhancement is available.
+     */
+    public boolean hasDialogueEnhancement() {
+        return mDialogueEnhancementAvailable;
+    }
+}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index eb6e830..d0963cb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,12 +16,15 @@
 
 package android.media;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -35,6 +38,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -1601,6 +1605,32 @@
         }
     }
 
+    //--------------------------------------------------------------------------
+    // Microphone information
+    //--------------------
+    /**
+     * Returns a lists of {@link MicrophoneInfo} representing the active microphones.
+     * By querying channel mapping for each active microphone, developer can know how
+     * the microphone is used by each channels or a capture stream.
+     * Note that the information about the active microphones may change during a recording.
+     * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes
+     * in the audio devices, querying the active microphones then will return the latest
+     * information.
+     *
+     * @return a lists of {@link MicrophoneInfo} representing the active microphones.
+     * @throws IOException if an error occurs
+     */
+    public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+        int status = native_get_active_microphones(activeMicrophones);
+        if (status != AudioManager.SUCCESS) {
+            Log.e(TAG, "getActiveMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>();
+        }
+        AudioManager.setPortIdForMicrophones(activeMicrophones);
+        return activeMicrophones;
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -1746,6 +1776,9 @@
     private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
             @AudioTimestamp.Timebase int timebase);
 
+    private native final int native_get_active_microphones(
+            ArrayList<MicrophoneInfo> activeMicrophones);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index dcd37da..be9fcb8 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -827,6 +827,8 @@
     private static native boolean native_is_offload_supported(int encoding, int sampleRate,
             int channelMask, int channelIndexMask);
 
+    public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4e9ce8e..8e822a5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2008,6 +2008,25 @@
     }
 
     /**
+     * Sets the audio presentation.
+     * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
+     * If a multi-stream decoder (MSD) is not present, or the format does not support
+     * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+     * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
+     *    {@link #ERROR_INVALID_OPERATION}
+     * @throws IllegalArgumentException if the audio presentation is null.
+     * @throws IllegalStateException if track is not initialized.
+     */
+    public int setPresentation(@NonNull AudioPresentation presentation) {
+        if (presentation == null) {
+            throw new IllegalArgumentException("audio presentation is null");
+        }
+        return native_setPresentation(presentation.getPresentationId(),
+                presentation.getProgramId());
+    }
+
+    /**
      * Sets the initialization state of the instance. This method was originally intended to be used
      * in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
      * However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
@@ -3245,6 +3264,7 @@
             @NonNull VolumeShaper.Operation operation);
 
     private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+    private native final int native_setPresentation(int presentationId, int programId);
 
     //---------------------------------------------------------
     // Utility methods
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 5ad4313..5cb8313 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -19,7 +19,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.MediaBrowser2Provider;
@@ -41,14 +40,14 @@
      */
     public static class BrowserCallback extends MediaController2.ControllerCallback {
         /**
-         * Called with the result of {@link #getBrowserRoot(Bundle)}.
+         * Called with the result of {@link #getLibraryRoot(Bundle)}.
          * <p>
-         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't
          * available.
          *
          * @param rootHints rootHints that you previously requested.
-         * @param rootMediaId media id of the browser root. Can be {@code null}
-         * @param rootExtra extra of the browser root. Can be {@code null}
+         * @param rootMediaId media id of the library root. Can be {@code null}
+         * @param rootExtra extra of the library root. Can be {@code null}
          */
         public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
                 @Nullable Bundle rootExtra) { }
@@ -114,8 +113,15 @@
                 .createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback);
     }
 
-    public void getBrowserRoot(Bundle rootHints) {
-        mProvider.getBrowserRoot_impl(rootHints);
+    /**
+     * Get the library root. Result would be sent back asynchronously with the
+     * {@link BrowserCallback#onGetRootResult(Bundle, String, Bundle)}.
+     *
+     * @param rootHints hint for the root
+     * @see BrowserCallback#onGetRootResult(Bundle, String, Bundle)
+     */
+    public void getLibraryRoot(Bundle rootHints) {
+        mProvider.getLibraryRoot_impl(rootHints);
     }
 
     /**
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 47dbde9..b32e539 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -30,6 +30,7 @@
 import android.media.session.MediaSessionManager;
 import android.media.update.ApiLoader;
 import android.media.update.MediaController2Provider;
+import android.media.update.PlaybackInfoProvider;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -160,21 +161,14 @@
          */
         public static final int PLAYBACK_TYPE_LOCAL = 1;
 
-        private final int mVolumeType;
-        private final int mVolumeControl;
-        private final int mMaxVolume;
-        private final int mCurrentVolume;
-        private final AudioAttributes mAudioAttrs;
+        private final PlaybackInfoProvider mProvider;
 
         /**
          * @hide
          */
-        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
-            mVolumeType = type;
-            mAudioAttrs = attrs;
-            mVolumeControl = control;
-            mMaxVolume = max;
-            mCurrentVolume = current;
+        @SystemApi
+        public PlaybackInfo(PlaybackInfoProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -187,7 +181,7 @@
          * @return The type of playback this session is using.
          */
         public int getPlaybackType() {
-            return mVolumeType;
+            return mProvider.getPlaybackType_impl();
         }
 
         /**
@@ -199,7 +193,7 @@
          * @return The attributes for this session.
          */
         public AudioAttributes getAudioAttributes() {
-            return mAudioAttrs;
+            return mProvider.getAudioAttributes_impl();
         }
 
         /**
@@ -210,11 +204,10 @@
          * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
          * </ul>
          *
-         * @return The type of volume control that may be used with this
-         *         session.
+         * @return The type of volume control that may be used with this session.
          */
-        public int getVolumeControl() {
-            return mVolumeControl;
+        public int getControlType() {
+            return mProvider.getControlType_impl();
         }
 
         /**
@@ -223,7 +216,7 @@
          * @return The maximum allowed volume where this session is playing.
          */
         public int getMaxVolume() {
-            return mMaxVolume;
+            return mProvider.getMaxVolume_impl();
         }
 
         /**
@@ -232,7 +225,7 @@
          * @return The current volume where this session is playing.
          */
         public int getCurrentVolume() {
-            return mCurrentVolume;
+            return mProvider.getCurrentVolume_impl();
         }
     }
 
@@ -277,6 +270,9 @@
         mProvider.close_impl();
     }
 
+    /**
+     * @hide
+     */
     @SystemApi
     public MediaController2Provider getProvider() {
         return mProvider;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 174d6a3..4919eeb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.media.AudioPresentation;
 import android.media.MediaCodec;
 import android.media.MediaFormat;
 import android.media.MediaHTTPService;
@@ -40,6 +41,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -396,6 +398,17 @@
     }
 
     /**
+     * Get the list of available audio presentations for the track.
+     * @param trackIndex index of the track.
+     * @return a list of available audio presentations for a given valid audio track index.
+     * The list will be empty if the source does not contain any audio presentations.
+     */
+    @NonNull
+    public List<AudioPresentation> getAudioPresentations(int trackIndex) {
+        return new ArrayList<AudioPresentation>();
+    }
+
+    /**
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
      * the crypto scheme, and the bytes being the data specific to that scheme.
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 2e9894b..eae4436 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -24,7 +24,6 @@
 import android.media.update.ApiLoader;
 import android.media.update.MediaItem2Provider;
 import android.os.Bundle;
-import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -69,7 +68,7 @@
     public MediaItem2(@NonNull Context context, @NonNull String mediaId,
             @NonNull DataSourceDesc dsd, @Nullable MediaMetadata2 metadata,
             @Flags int flags) {
-        mProvider = ApiLoader.getProvider(context).createMediaItem2Provider(
+        mProvider = ApiLoader.getProvider(context).createMediaItem2(
                 context, this, mediaId, dsd, metadata, flags);
     }
 
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 79b105f..f88f9f2 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -19,11 +19,13 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.media.MediaSession2.BuilderBase;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSessionService2Provider;
@@ -63,26 +65,16 @@
     /**
      * Session for the media library service.
      */
-    public class MediaLibrarySession extends MediaSession2 {
+    public static class MediaLibrarySession extends MediaSession2 {
         private final MediaLibrarySessionProvider mProvider;
 
-        MediaLibrarySession(Context context, MediaPlayerInterface player, String id,
-                VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
-                Executor callbackExecutor, SessionCallback callback) {
-            super(context, player, id, volumeProvider, ratingType, sessionActivity,
-                    callbackExecutor, callback);
-            mProvider = (MediaLibrarySessionProvider) getProvider();
-        }
-
-        @Override
-        MediaSession2Provider createProvider(Context context, MediaPlayerInterface player,
-                String id, VolumeProvider volumeProvider, int ratingType,
-                PendingIntent sessionActivity, Executor callbackExecutor,
-                SessionCallback callback) {
-            return ApiLoader.getProvider(context)
-                    .createMediaLibraryService2MediaLibrarySession(context, this, player, id,
-                            volumeProvider, ratingType, sessionActivity,
-                            callbackExecutor, (MediaLibrarySessionCallback) callback);
+        /**
+         * @hide
+         */
+        @SystemApi
+        public MediaLibrarySession(MediaLibrarySessionProvider provider) {
+            super(provider);
+            mProvider = provider;
         }
 
         /**
@@ -125,15 +117,15 @@
          *
          * @param controllerInfo information of the controller requesting access to browse media.
          * @param rootHints An optional bundle of service-specific arguments to send
-         * to the media browser service when connecting and retrieving the
+         * to the media library service when connecting and retrieving the
          * root id for browsing, or null if none. The contents of this
          * bundle may affect the information returned when browsing.
-         * @return The {@link BrowserRoot} for accessing this app's content or null.
-         * @see BrowserRoot#EXTRA_RECENT
-         * @see BrowserRoot#EXTRA_OFFLINE
-         * @see BrowserRoot#EXTRA_SUGGESTED
+         * @return The {@link LibraryRoot} for accessing this app's content or null.
+         * @see LibraryRoot#EXTRA_RECENT
+         * @see LibraryRoot#EXTRA_OFFLINE
+         * @see LibraryRoot#EXTRA_SUGGESTED
          */
-        public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
+        public @Nullable LibraryRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
                 @Nullable Bundle rootHints) {
             return null;
         }
@@ -208,31 +200,15 @@
     /**
      * Builder for {@link MediaLibrarySession}.
      */
-    // TODO(jaewan): Move this to updatable.
-    public class MediaLibrarySessionBuilder
-            extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
+    public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
+            MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
         public MediaLibrarySessionBuilder(
                 @NonNull Context context, @NonNull MediaPlayerInterface player,
                 @NonNull @CallbackExecutor Executor callbackExecutor,
                 @NonNull MediaLibrarySessionCallback callback) {
-            super(context, player);
-            setSessionCallback(callbackExecutor, callback);
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setSessionCallback(
-                @NonNull @CallbackExecutor Executor callbackExecutor,
-                @NonNull MediaLibrarySessionCallback callback) {
-            if (callback == null) {
-                throw new IllegalArgumentException("MediaLibrarySessionCallback cannot be null");
-            }
-            return super.setSessionCallback(callbackExecutor, callback);
-        }
-
-        @Override
-        public MediaLibrarySession build() {
-            return new MediaLibrarySession(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
-                    mSessionActivity, mCallbackExecutor, mCallback);
+            super((instance) -> ApiLoader.getProvider(context).createMediaLibraryService2Builder(
+                    context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
+                    callback));
         }
     }
 
@@ -262,17 +238,17 @@
     public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
 
     /**
-     * Contains information that the browser service needs to send to the client
-     * when first connected.
+     * Contains information that the library service needs to send to the client when
+     * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
      */
-    public static final class BrowserRoot {
+    public static final class LibraryRoot {
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for recently played media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * librar root for recently played media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are recently played.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -284,13 +260,13 @@
         public static final String EXTRA_RECENT = "android.media.extra.RECENT";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for offline media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for offline media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are can be played without an
          * internet connection.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -302,14 +278,14 @@
         public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for suggested media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for suggested media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving the media items suggested by the media browser
+         * <p>When creating a media browser for a given media library service, this key can be
+         * supplied as a root hint for retrieving the media items suggested by the media library
          * service. The list of media items is considered ordered by relevance, first being the top
          * suggestion.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -320,35 +296,31 @@
          */
         public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
 
-        final private String mRootId;
-        final private Bundle mExtras;
+        private final LibraryRootProvider mProvider;
 
         /**
-         * Constructs a browser root.
+         * Constructs a library root.
          * @param rootId The root id for browsing.
-         * @param extras Any extras about the browser service.
+         * @param extras Any extras about the library service.
          */
-        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
-            if (rootId == null) {
-                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
-                        "Use null for BrowserRoot instead.");
-            }
-            mRootId = rootId;
-            mExtras = extras;
+        public LibraryRoot(@NonNull Context context,
+                @NonNull String rootId, @Nullable Bundle extras) {
+            mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot(
+                    context, this, rootId, extras);
         }
 
         /**
          * Gets the root id for browsing.
          */
         public String getRootId() {
-            return mRootId;
+            return mProvider.getRootId_impl();
         }
 
         /**
-         * Gets any extras about the browser service.
+         * Gets any extras about the library service.
          */
         public Bundle getExtras() {
-            return mExtras;
+            return mProvider.getExtras_impl();
         }
     }
 }
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index fcdb4f7..54a9057 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -16,17 +16,16 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.content.Context;
 import android.graphics.Bitmap;
+import android.media.update.ApiLoader;
+import android.media.update.MediaMetadata2Provider;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.ArrayMap;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -37,9 +36,11 @@
  *
  * @hide
  */
-// TODO(jaewan): Move this to updatable
 public final class MediaMetadata2 {
-    private static final String TAG = "MediaMetadata2";
+    // New version of MediaMetadata that no longer implements Parcelable but added from/toBundle()
+    // for updatable.
+    // MediaDescription is deprecated because it was insufficient for controller to display media
+    // contents. Added getExtra() here to support all the features from the MediaDescription.
 
     /**
      * The title of the media.
@@ -365,76 +366,14 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface RatingKey {}
 
-    static final int METADATA_TYPE_LONG = 0;
-    static final int METADATA_TYPE_TEXT = 1;
-    static final int METADATA_TYPE_BITMAP = 2;
-    static final int METADATA_TYPE_RATING = 3;
-    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
-
-    static {
-        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
-        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
-    }
-
-    private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
-            METADATA_KEY_TITLE,
-            METADATA_KEY_ARTIST,
-            METADATA_KEY_ALBUM,
-            METADATA_KEY_ALBUM_ARTIST,
-            METADATA_KEY_WRITER,
-            METADATA_KEY_AUTHOR,
-            METADATA_KEY_COMPOSER
-    };
-
-    private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
-            METADATA_KEY_DISPLAY_ICON,
-            METADATA_KEY_ART,
-            METADATA_KEY_ALBUM_ART
-    };
-
-    private static final @TextKey String[] PREFERRED_URI_ORDER = {
-            METADATA_KEY_DISPLAY_ICON_URI,
-            METADATA_KEY_ART_URI,
-            METADATA_KEY_ALBUM_ART_URI
-    };
-
-    final Bundle mBundle;
+    private final MediaMetadata2Provider mProvider;
 
     /**
      * @hide
      */
-    public MediaMetadata2(Bundle bundle) {
-        mBundle = new Bundle(bundle);
+    @SystemApi
+    public MediaMetadata2(MediaMetadata2Provider provider) {
+        mProvider = provider;
     }
 
     /**
@@ -443,8 +382,8 @@
      * @param key a String key
      * @return true if the key exists in this metadata, false otherwise
      */
-    public boolean containsKey(String key) {
-        return mBundle.containsKey(key);
+    public boolean containsKey(@NonNull String key) {
+        return mProvider.containsKey_impl(key);
     }
 
     /**
@@ -455,8 +394,8 @@
      * @param key The key the value is stored under
      * @return a CharSequence value, or null
      */
-    public CharSequence getText(@TextKey String key) {
-        return mBundle.getCharSequence(key);
+    public @Nullable CharSequence getText(@TextKey String key) {
+        return mProvider.getText_impl(key);
     }
 
     /**
@@ -464,11 +403,11 @@
      * the desired type exists for the given key or a null value is explicitly
      * associated with the key.
      *
-     * @
      * @return media id. Can be {@code null}
+     * @see #METADATA_KEY_MEDIA_ID
      */
     public @Nullable String getMediaId() {
-        return getString(METADATA_KEY_MEDIA_ID);
+        return mProvider.getMediaId_impl();
     }
 
     /**
@@ -479,12 +418,8 @@
      * @param key The key the value is stored under
      * @return a String value, or null
      */
-    public String getString(@TextKey String key) {
-        CharSequence text = mBundle.getCharSequence(key);
-        if (text != null) {
-            return text.toString();
-        }
-        return null;
+    public @Nullable String getString(@NonNull @TextKey String key) {
+        return mProvider.getString_impl(key);
     }
 
     /**
@@ -494,8 +429,8 @@
      * @param key The key the value is stored under
      * @return a long value
      */
-    public long getLong(@LongKey String key) {
-        return mBundle.getLong(key, 0);
+    public long getLong(@NonNull @LongKey String key) {
+        return mProvider.getLong_impl(key);
     }
 
     /**
@@ -503,18 +438,10 @@
      * the given key.
      *
      * @param key The key the value is stored under
-     * @return A {@link Rating2} or null
+     * @return A {@link Rating2} or {@code null}
      */
-    public Rating2 getRating(@RatingKey String key) {
-        // TODO(jaewan): Add backward compatibility
-        Rating2 rating = null;
-        try {
-            rating = Rating2.fromBundle(mBundle.getBundle(key));
-        } catch (Exception e) {
-            // ignore, value was not a rating
-            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
-        }
-        return rating;
+    public @Nullable Rating2 getRating(@RatingKey String key) {
+        return mProvider.getRating_impl(key);
     }
 
     /**
@@ -525,14 +452,7 @@
      * @return A {@link Bitmap} or null
      */
     public Bitmap getBitmap(@BitmapKey String key) {
-        Bitmap bmp = null;
-        try {
-            bmp = mBundle.getParcelable(key);
-        } catch (Exception e) {
-            // ignore, value was not a bitmap
-            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
-        }
-        return bmp;
+        return mProvider.getBitmap_impl(key);
     }
 
     /**
@@ -540,14 +460,8 @@
      *
      * @return A {@link Bundle} or {@code null}
      */
-    public Bundle getExtra() {
-        try {
-            return mBundle.getBundle(METADATA_KEY_EXTRA);
-        } catch (Exception e) {
-            // ignore, value was not an bundle
-            Log.w(TAG, "Failed to retrieve an extra");
-        }
-        return null;
+    public @Nullable Bundle getExtra() {
+        return mProvider.getExtra_impl();
     }
 
     /**
@@ -556,7 +470,7 @@
      * @return The number of fields in the metadata.
      */
     public int size() {
-        return mBundle.size();
+        return mProvider.size_impl();
     }
 
     /**
@@ -564,8 +478,8 @@
      *
      * @return a Set of String keys
      */
-    public Set<String> keySet() {
-        return mBundle.keySet();
+    public @NonNull Set<String> keySet() {
+        return mProvider.keySet_impl();
     }
 
     /**
@@ -574,8 +488,21 @@
      *
      * @return The Bundle backing this metadata.
      */
-    public Bundle getBundle() {
-        return mBundle;
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    /**
+     * Creates the {@link MediaMetadata2} from the bundle that previously returned by
+     * {@link #toBundle()}.
+     *
+     * @param context context
+     * @param bundle bundle for the metadata
+     * @return a new MediaMetadata2
+     */
+    public static @NonNull MediaMetadata2 fromBundle(@NonNull Context context,
+            @Nullable Bundle bundle) {
+        return ApiLoader.getProvider(context).fromBundle_MediaMetadata2(context, bundle);
     }
 
     /**
@@ -583,14 +510,15 @@
      * use the appropriate data type.
      */
     public static final class Builder {
-        private final Bundle mBundle;
+        private final MediaMetadata2Provider.BuilderProvider mProvider;
 
         /**
          * Create an empty Builder. Any field that should be included in the
          * {@link MediaMetadata2} must be added.
          */
-        public Builder() {
-            mBundle = new Bundle();
+        public Builder(@NonNull Context context) {
+            mProvider = ApiLoader.getProvider(context).createMediaMetadata2Builder(
+                    context, this);
         }
 
         /**
@@ -600,31 +528,17 @@
          *
          * @param source
          */
-        public Builder(MediaMetadata2 source) {
-            mBundle = new Bundle(source.mBundle);
+        public Builder(@NonNull Context context, @NonNull MediaMetadata2 source) {
+            mProvider = ApiLoader.getProvider(context).createMediaMetadata2Builder(
+                    context, this, source);
         }
 
         /**
-         * Create a Builder using a {@link MediaMetadata2} instance to set
-         * initial values, but replace bitmaps with a scaled down copy if they
-         * are larger than maxBitmapSize.
-         *
-         * @param source The original metadata to copy.
-         * @param maxBitmapSize The maximum height/width for bitmaps contained
-         *            in the metadata.
          * @hide
          */
-        public Builder(MediaMetadata2 source, int maxBitmapSize) {
-            this(source);
-            for (String key : mBundle.keySet()) {
-                Object value = mBundle.get(key);
-                if (value instanceof Bitmap) {
-                    Bitmap bmp = (Bitmap) value;
-                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
-                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
-                    }
-                }
-            }
+        @SystemApi
+        public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -653,15 +567,8 @@
          * @param value The CharSequence value to store
          * @return The Builder to allow chaining
          */
-        public Builder putText(@TextKey String key, CharSequence value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a CharSequence");
-                }
-            }
-            mBundle.putCharSequence(key, value);
-            return this;
+        public @NonNull Builder putText(@TextKey String key, @Nullable CharSequence value) {
+            return mProvider.putText_impl(key, value);
         }
 
         /**
@@ -690,15 +597,8 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putString(@TextKey String key, String value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a String");
-                }
-            }
-            mBundle.putCharSequence(key, value);
-            return this;
+        public @NonNull Builder putString(@TextKey String key, @Nullable String value) {
+            return mProvider.putString_impl(key, value);
         }
 
         /**
@@ -720,15 +620,8 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putLong(@LongKey String key, long value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a long");
-                }
-            }
-            mBundle.putLong(key, value);
-            return this;
+        public @NonNull Builder putLong(@NonNull @LongKey String key, long value) {
+            return mProvider.putLong_impl(key, value);
         }
 
         /**
@@ -744,16 +637,8 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putRating(@RatingKey String key, Rating2 value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a Rating");
-                }
-            }
-            mBundle.putBundle(key, value.toBundle());
-
-            return this;
+        public @NonNull Builder putRating(@NonNull @RatingKey String key, @Nullable Rating2 value) {
+            return mProvider.putRating_impl(key, value);
         }
 
         /**
@@ -774,23 +659,15 @@
          * @param value The Bitmap to store
          * @return The Builder to allow chaining
          */
-        public Builder putBitmap(@BitmapKey String key, Bitmap value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a Bitmap");
-                }
-            }
-            mBundle.putParcelable(key, value);
-            return this;
+        public @NonNull Builder putBitmap(@NonNull @BitmapKey String key, @Nullable Bitmap value) {
+            return mProvider.putBitmap_impl(key, value);
         }
 
         /**
          * Set an extra {@link Bundle} into the metadata.
          */
-        public Builder setExtra(Bundle bundle) {
-            mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
-            return this;
+        public @NonNull Builder setExtra(@Nullable Bundle bundle) {
+            return mProvider.setExtra_impl(bundle);
         }
 
         /**
@@ -798,18 +675,8 @@
          *
          * @return The new MediaMetadata2 instance
          */
-        public MediaMetadata2 build() {
-            return new MediaMetadata2(mBundle);
-        }
-
-        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
-            float maxSizeF = maxSize;
-            float widthScale = maxSizeF / bmp.getWidth();
-            float heightScale = maxSizeF / bmp.getHeight();
-            float scale = Math.min(widthScale, heightScale);
-            int height = (int) (bmp.getHeight() * scale);
-            int width = (int) (bmp.getWidth() * scale);
-            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        public @NonNull MediaMetadata2 build() {
+            return mProvider.build_impl();
         }
     }
 }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 78477f7..62240ce 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Surface;
@@ -34,6 +35,8 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -1406,6 +1409,31 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
 
+    //--------------------------------------------------------------------------
+    // Microphone information
+    //--------------------
+    /**
+     * Return A lists of {@link MicrophoneInfo} representing the active microphones.
+     * By querying channel mapping for each active microphone, developer can know how
+     * the microphone is used by each channels or a capture stream.
+     *
+     * @return a lists of {@link MicrophoneInfo} representing the active microphones
+     * @throws IOException if an error occurs
+     */
+    public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+        int status = native_getActiveMicrophones(activeMicrophones);
+        if (status != AudioManager.SUCCESS) {
+            Log.e(TAG, "getActiveMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>();
+        }
+        AudioManager.setPortIdForMicrophones(activeMicrophones);
+        return activeMicrophones;
+    }
+
+    private native final int native_getActiveMicrophones(
+            ArrayList<MicrophoneInfo> activeMicrophones);
+
     /**
      * Called from native code when an interesting event happens.  This method
      * just uses the EventHandler system to post the event back to the main app thread.
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 0ea1e86..5670bd8 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -30,9 +30,12 @@
 import android.media.session.PlaybackState;
 import android.media.update.ApiLoader;
 import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.media.update.MediaSession2Provider.CommandGroupProvider;
 import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.media.update.ProviderCreator;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -81,34 +84,178 @@
 public class MediaSession2 implements AutoCloseable {
     private final MediaSession2Provider mProvider;
 
-    // Note: Do not define IntDef because subclass can add more command code on top of these.
+    // TODO(jaewan): Should we define IntDef? Currently we don't have to allow subclass to add more.
     // TODO(jaewan): Shouldn't we pull out?
-    // TODO(jaewan): Should we also protect getPlaybackState()?
+    // TODO(jaewan): Should we also protect getters not related with metadata?
+    //               Getters are getRatingType(), getPlaybackState(), getSessionActivity(),
+    //               getPlaylistParams())
+    // Next ID: 23
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link Command}.
+     */
     public static final int COMMAND_CODE_CUSTOM = 0;
-    public static final int COMMAND_CODE_PLAYBACK_START = 1;
+
+    /**
+     * Command code for {@link MediaController2#play()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
+
+    /**
+     * Command code for {@link MediaController2#pause()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+
+    /**
+     * Command code for {@link MediaController2#stop()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+
+    /**
+     * Command code for {@link MediaController2#skipToNext()} ()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
+
+    /**
+     * Command code for {@link MediaController2#skipToPrevious()} ()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Command code for {@link MediaController2#prepare()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
+
+    /**
+     * Command code for {@link MediaController2#fastForward()} ()}.
+     * <p>
+     * This is transport control command. Command would be sent directly to the player if the
+     * session doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7;
+
+    /**
+     * Command code for {@link MediaController2#rewind()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
+
+    /**
+     * Command code for {@link MediaController2#seekTo(long)} ()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
+    /**
+     * Command code for {@link MediaController2#setCurrentPlaylistItem(int)} ()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10;
 
-    public static final int COMMAND_CODE_PLAYLIST_GET = 11;
+    /**
+     * Command code for {@link MediaController2#setPlaylistParams(PlaylistParams)} ()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 11;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYLIST_ADD = 12;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
     public static final int COMMAND_CODE_PLAYLIST_REMOVE = 13;
 
-    public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 14;
-    public static final int COMMAND_CODE_PLAY_FROM_URI = 15;
-    public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 16;
+    /**
+     * Command code for {@link MediaController2#getPlaylist()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET = 14;
 
-    public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 17;
-    public static final int COMMAND_CODE_PREPARE_FROM_URI = 18;
-    public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 19;
+    /**
+     * Command code for both {@link MediaController2#setVolumeTo(int, int)} and
+     * {@link MediaController2#adjustVolume(int, int)}.
+     * <p>
+     * Command would adjust the volume or sent to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
+     */
+    public static final int COMMAND_CODE_SET_VOLUME = 15;
 
-    public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 20;
+    /**
+     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16;
+
+    /**
+     * Command code for {@link MediaController2#playFromUri(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PLAY_FROM_URI = 17;
+
+    /**
+     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 18;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 19;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PREPARE_FROM_URI = 20;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 21;
+
+    /**
+     * Command code for {@link MediaBrowser2} specific functions that allows navigation and search
+     * from the {@link MediaLibraryService2}. This would be ignored if a {@link MediaSession2},
+     * not {@link android.media.MediaLibraryService2.MediaLibrarySession}, specify this.
+     *
+     * @see MediaBrowser2
+     */
+    public static final int COMMAND_CODE_BROWSER = 22;
 
     /**
      * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
@@ -212,6 +359,9 @@
             return mProvider.hasCommand_impl(code);
         }
 
+        /**
+         * @hide
+         */
         @SystemApi
         public CommandGroupProvider getProvider() {
             return mProvider;
@@ -274,22 +424,36 @@
         public void onDisconnected(@NonNull ControllerInfo controller) { }
 
         /**
-         * Called when a controller sent a command to the session, and the command will be sent to
-         * the player directly unless you reject the request by {@code false}.
+         * Called when a controller sent a command that will be sent directly to the player. Return
+         * {@code false} here to reject the request and stop sending command to the player.
          *
          * @param controller controller information.
          * @param command a command. This method will be called for every single command.
          * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
+         * @see #COMMAND_CODE_PLAYBACK_PLAY
+         * @see #COMMAND_CODE_PLAYBACK_PAUSE
+         * @see #COMMAND_CODE_PLAYBACK_STOP
+         * @see #COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM
+         * @see #COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM
+         * @see #COMMAND_CODE_PLAYBACK_PREPARE
+         * @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
+         * @see #COMMAND_CODE_PLAYBACK_REWIND
+         * @see #COMMAND_CODE_PLAYBACK_SEEK_TO
+         * @see #COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM
+         * @see #COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS
+         * @see #COMMAND_CODE_PLAYLIST_ADD
+         * @see #COMMAND_CODE_PLAYLIST_REMOVE
+         * @see #COMMAND_CODE_PLAYLIST_GET
+         * @see #COMMAND_CODE_SET_VOLUME
          */
-        // TODO(jaewan): Add more documentations (or make it clear) which commands can be filtered
-        //               with this.
         public boolean onCommandRequest(@NonNull ControllerInfo controller,
                 @NonNull Command command) {
             return true;
         }
 
         /**
-         * Called when a controller set rating on the currently playing contents.
+         * Called when a controller set rating on the currently playing contents by
+         * {@link MediaController2#setRating(Rating2)}.
          *
          * @param controller controller information
          * @param rating new rating from the controller
@@ -297,7 +461,8 @@
         public void onSetRating(@NonNull ControllerInfo controller, @NonNull Rating2 rating) { }
 
         /**
-         * Called when a controller sent a custom command.
+         * Called when a controller sent a custom command through
+         * {@link MediaController2#sendCustomCommand(Command, Bundle, ResultReceiver)}.
          *
          * @param controller controller information
          * @param customCommand custom command.
@@ -309,7 +474,48 @@
                 @Nullable ResultReceiver cb) { }
 
         /**
-         * Override to handle requests to prepare for playing a specific mediaId.
+         * Called when a controller requested to play a specific mediaId through
+         * {@link MediaController2#playFromMediaId(String, Bundle)}.
+         *
+         * @param controller controller information
+         * @param mediaId media id
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PLAY_FROM_MEDIA_ID
+         */
+        public void onPlayFromMediaId(@NonNull ControllerInfo controller,
+                @NonNull String mediaId, @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to begin playback from a search query through
+         * {@link MediaController2#playFromSearch(String, Bundle)}
+         * <p>
+         * An empty query indicates that the app may play any music. The implementation should
+         * attempt to make a smart choice about what to play.
+         *
+         * @param controller controller information
+         * @param query query string. Can be empty to indicate any suggested media
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PLAY_FROM_SEARCH
+         */
+        public void onPlayFromSearch(@NonNull ControllerInfo controller,
+                @NonNull String query, @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to play a specific media item represented by a URI
+         * through {@link MediaController2#playFromUri(String, Bundle)}
+         *
+         * @param controller controller information
+         * @param uri uri
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PLAY_FROM_URI
+         */
+        public void onPlayFromUri(@NonNull ControllerInfo controller,
+                @NonNull String uri, @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to prepare for playing a specific mediaId through
+         * {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+         * <p>
          * During the preparation, a session should not hold audio focus in order to allow other
          * sessions play seamlessly. The state of playback should be updated to
          * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
@@ -319,28 +525,41 @@
          * <p>
          * Override {@link #onPlayFromMediaId} to handle requests for starting
          * playback without preparation.
+         *
+         * @param controller controller information
+         * @param mediaId media id to prepare
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PREPARE_FROM_MEDIA_ID
          */
-        public void onPlayFromMediaId(@NonNull ControllerInfo controller,
+        public void onPrepareFromMediaId(@NonNull ControllerInfo controller,
                 @NonNull String mediaId, @Nullable Bundle extras) { }
 
         /**
-         * Override to handle requests to prepare playback from a search query. An empty query
-         * indicates that the app may prepare any music. The implementation should attempt to make a
-         * smart choice about what to play. During the preparation, a session should not hold audio
-         * focus in order to allow other sessions play seamlessly. The state of playback should be
-         * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * Called when a controller requested to prepare playback from a search query through
+         * {@link MediaController2#prepareFromSearch(String, Bundle)}.
          * <p>
-         * The playback of the prepared content should start in the later calls of
-         * {@link MediaSession2#play()}.
+         * An empty query indicates that the app may prepare any music. The implementation should
+         * attempt to make a smart choice about what to play.
+         * <p>
+         * The state of playback should be updated to {@link PlaybackState#STATE_PAUSED} after the
+         * preparation is done. The playback of the prepared content should start in the later
+         * calls of {@link MediaSession2#play()}.
          * <p>
          * Override {@link #onPlayFromSearch} to handle requests for starting playback without
          * preparation.
+         *
+         * @param controller controller information
+         * @param query query string. Can be empty to indicate any suggested media
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PREPARE_FROM_SEARCH
          */
-        public void onPlayFromSearch(@NonNull ControllerInfo controller,
+        public void onPrepareFromSearch(@NonNull ControllerInfo controller,
                 @NonNull String query, @Nullable Bundle extras) { }
 
         /**
-         * Override to handle requests to prepare a specific media item represented by a URI.
+         * Called when a controller requested to prepare a specific media item represented by a URI
+         * through {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+         * <p></p>
          * During the preparation, a session should not hold audio focus in order to allow
          * other sessions play seamlessly. The state of playback should be updated to
          * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
@@ -350,51 +569,14 @@
          * <p>
          * Override {@link #onPlayFromUri} to handle requests for starting playback without
          * preparation.
-         */
-        public void onPlayFromUri(@NonNull ControllerInfo controller,
-                @NonNull String uri, @Nullable Bundle extras) { }
-
-        /**
-         * Override to handle requests to play a specific mediaId.
-         */
-        public void onPrepareFromMediaId(@NonNull ControllerInfo controller,
-                @NonNull String mediaId, @Nullable Bundle extras) { }
-
-        /**
-         * Override to handle requests to begin playback from a search query. An
-         * empty query indicates that the app may play any music. The
-         * implementation should attempt to make a smart choice about what to
-         * play.
-         */
-        public void onPrepareFromSearch(@NonNull ControllerInfo controller,
-                @NonNull String query, @Nullable Bundle extras) { }
-
-        /**
-         * Override to handle requests to play a specific media item represented by a URI.
+         *
+         * @param controller controller information
+         * @param uri uri
+         * @param extras optional extra bundle
+         * @see #COMMAND_CODE_PREPARE_FROM_URI
          */
         public void onPrepareFromUri(@NonNull ControllerInfo controller,
                 @NonNull Uri uri, @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller wants to add a {@link MediaItem2} at the specified position
-         * in the play queue.
-         * <p>
-         * The item from the media controller wouldn't have valid data source descriptor because
-         * it would have been anonymized when it's sent to the remote process.
-         *
-         * @param item The media item to be inserted.
-         * @param index The index at which the item is to be inserted.
-         */
-        public void onAddPlaylistItem(@NonNull ControllerInfo controller,
-                @NonNull MediaItem2 item, int index) { }
-
-        /**
-         * Called when a controller wants to remove the {@link MediaItem2}
-         *
-         * @param item
-         */
-        // Can we do this automatically?
-        public void onRemovePlaylistItem(@NonNull MediaItem2 item) { }
     };
 
     /**
@@ -403,36 +585,11 @@
      * @hide
      */
     static abstract class BuilderBase
-            <T extends MediaSession2.BuilderBase<T, C>, C extends SessionCallback> {
-        final Context mContext;
-        final MediaPlayerInterface mPlayer;
-        String mId;
-        Executor mCallbackExecutor;
-        C mCallback;
-        VolumeProvider mVolumeProvider;
-        int mRatingType;
-        PendingIntent mSessionActivity;
+            <T extends MediaSession2, U extends BuilderBase<T, U, C>, C extends SessionCallback> {
+        private final BuilderBaseProvider<T, C> mProvider;
 
-        /**
-         * Constructor.
-         *
-         * @param context a context
-         * @param player a player to handle incoming command from any controller.
-         * @throws IllegalArgumentException if any parameter is null, or the player is a
-         *      {@link MediaSession2} or {@link MediaController2}.
-         */
-        // TODO(jaewan): Also need executor
-        public BuilderBase(@NonNull Context context, @NonNull MediaPlayerInterface player) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            if (player == null) {
-                throw new IllegalArgumentException("player shouldn't be null");
-            }
-            mContext = context;
-            mPlayer = player;
-            // Ensure non-null
-            mId = "";
+        BuilderBase(ProviderCreator<BuilderBase<T, U, C>, BuilderBaseProvider<T, C>> creator) {
+            mProvider = creator.createProvider(this);
         }
 
         /**
@@ -444,9 +601,9 @@
          *
          * @param volumeProvider The provider that will handle volume changes. Can be {@code null}
          */
-        public T setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
-            mVolumeProvider = volumeProvider;
-            return (T) this;
+        public U setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
+            mProvider.setVolumeProvider_impl(volumeProvider);
+            return (U) this;
         }
 
         /**
@@ -462,9 +619,9 @@
          * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
          * </ul>
          */
-        public T setRatingType(@Rating2.Style int type) {
-            mRatingType = type;
-            return (T) this;
+        public U setRatingType(@Rating2.Style int type) {
+            mProvider.setRatingType_impl(type);
+            return (U) this;
         }
 
         /**
@@ -474,9 +631,9 @@
          *
          * @param pi The intent to launch to show UI for this session.
          */
-        public T setSessionActivity(@Nullable PendingIntent pi) {
-            mSessionActivity = pi;
-            return (T) this;
+        public U setSessionActivity(@Nullable PendingIntent pi) {
+            mProvider.setSessionActivity_impl(pi);
+            return (U) this;
         }
 
         /**
@@ -489,12 +646,9 @@
          * @throws IllegalArgumentException if id is {@code null}
          * @return
          */
-        public T setId(@NonNull String id) {
-            if (id == null) {
-                throw new IllegalArgumentException("id shouldn't be null");
-            }
-            mId = id;
-            return (T) this;
+        public U setId(@NonNull String id) {
+            mProvider.setId_impl(id);
+            return (U) this;
         }
 
         /**
@@ -504,17 +658,10 @@
          * @param callback session callback.
          * @return
          */
-        public T setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+        public U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
                 @NonNull C callback) {
-            if (executor == null) {
-                throw new IllegalArgumentException("executor shouldn't be null");
-            }
-            if (callback == null) {
-                throw new IllegalArgumentException("callback shouldn't be null");
-            }
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return (T) this;
+            mProvider.setSessionCallback_impl(executor, callback);
+            return (U) this;
         }
 
         /**
@@ -524,7 +671,9 @@
          * @throws IllegalStateException if the session with the same id is already exists for the
          *      package.
          */
-        public abstract MediaSession2 build();
+        public T build() {
+            return mProvider.build_impl();
+        }
     }
 
     /**
@@ -533,24 +682,12 @@
      * Any incoming event from the {@link MediaController2} will be handled on the thread
      * that created session with the {@link Builder#build()}.
      */
-    // TODO(jaewan): Move this to updatable
     // TODO(jaewan): Add setRatingType()
     // TODO(jaewan): Add setSessionActivity()
-    public static final class Builder extends BuilderBase<Builder, SessionCallback> {
+    public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
         public Builder(Context context, @NonNull MediaPlayerInterface player) {
-            super(context, player);
-        }
-
-        @Override
-        public MediaSession2 build() {
-            if (mCallbackExecutor == null) {
-                mCallbackExecutor = mContext.getMainExecutor();
-            }
-            if (mCallback == null) {
-                mCallback = new SessionCallback(mContext);
-            }
-            return new MediaSession2(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
-                    mSessionActivity, mCallbackExecutor, mCallback);
+            super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
+                    context, (Builder) instance, player));
         }
     }
 
@@ -568,7 +705,7 @@
         public ControllerInfo(Context context, int uid, int pid, String packageName,
                 IInterface callback) {
             mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2ControllerInfoProvider(
+                    .createMediaSession2ControllerInfo(
                             context, this, uid, pid, packageName, callback);
         }
 
@@ -597,6 +734,9 @@
             return mProvider.isTrusted_impl();
         }
 
+        /**
+         * @hide
+         */
         @SystemApi
         public ControllerInfoProvider getProvider() {
             return mProvider;
@@ -629,32 +769,15 @@
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
-    // TODO(jaewan): Move this to updatable.
     public static class CommandButton {
-        private static final String KEY_COMMAND
-                = "android.media.media_session2.command_button.command";
-        private static final String KEY_ICON_RES_ID
-                = "android.media.media_session2.command_button.icon_res_id";
-        private static final String KEY_DISPLAY_NAME
-                = "android.media.media_session2.command_button.display_name";
-        private static final String KEY_EXTRA
-                = "android.media.media_session2.command_button.extra";
-        private static final String KEY_ENABLED
-                = "android.media.media_session2.command_button.enabled";
+        private final CommandButtonProvider mProvider;
 
-        private Command mCommand;
-        private int mIconResId;
-        private String mDisplayName;
-        private Bundle mExtra;
-        private boolean mEnabled;
-
-        private CommandButton(@Nullable Command command, int iconResId,
-                @Nullable String displayName, Bundle extra, boolean enabled) {
-            mCommand = command;
-            mIconResId = iconResId;
-            mDisplayName = displayName;
-            mExtra = extra;
-            mEnabled = enabled;
+        /**
+         * @hide
+         */
+        @SystemApi
+        public CommandButton(CommandButtonProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -664,7 +787,7 @@
          * @return command or {@code null}
          */
         public @Nullable Command getCommand() {
-            return mCommand;
+            return mProvider.getCommand_impl();
         }
 
         /**
@@ -674,7 +797,7 @@
          * @return resource id of the icon. Can be {@code 0}.
          */
         public int getIconResId() {
-            return mIconResId;
+            return mProvider.getIconResId_impl();
         }
 
         /**
@@ -684,7 +807,7 @@
          * @return custom display name. Can be {@code null} or empty.
          */
         public @Nullable String getDisplayName() {
-            return mDisplayName;
+            return mProvider.getDisplayName_impl();
         }
 
         /**
@@ -693,7 +816,7 @@
          * @return
          */
         public @Nullable Bundle getExtra() {
-            return mExtra;
+            return mProvider.getExtra_impl();
         }
 
         /**
@@ -702,92 +825,50 @@
          * @return {@code true} if enabled. {@code false} otherwise.
          */
         public boolean isEnabled() {
-            return mEnabled;
+            return mProvider.isEnabled_impl();
         }
 
         /**
          * @hide
          */
-        // TODO(jaewan): @SystemApi
-        public @NonNull Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
-            bundle.putInt(KEY_ICON_RES_ID, mIconResId);
-            bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
-            bundle.putBundle(KEY_EXTRA, mExtra);
-            bundle.putBoolean(KEY_ENABLED, mEnabled);
-            return bundle;
-        }
-
-        /**
-         * @hide
-         */
-        // TODO(jaewan): @SystemApi
-        public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
-            Builder builder = new Builder();
-            builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
-            builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
-            builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
-            builder.setExtra(bundle.getBundle(KEY_EXTRA));
-            builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
-            try {
-                return builder.build();
-            } catch (IllegalStateException e) {
-                // Malformed or version mismatch. Return null for now.
-                return null;
-            }
+        @SystemApi
+        public CommandButtonProvider getProvider() {
+            return mProvider;
         }
 
         /**
          * Builder for {@link CommandButton}.
          */
         public static class Builder {
-            private Command mCommand;
-            private int mIconResId;
-            private String mDisplayName;
-            private Bundle mExtra;
-            private boolean mEnabled;
+            private final CommandButtonProvider.BuilderProvider mProvider;
 
-            public Builder() {
-                mEnabled = true;
+            public Builder(@NonNull Context context) {
+                mProvider = ApiLoader.getProvider(context)
+                        .createMediaSession2CommandButtonBuilder(context, this);
             }
 
             public Builder setCommand(Command command) {
-                mCommand = command;
-                return this;
+                return mProvider.setCommand_impl(command);
             }
 
             public Builder setIconResId(int resId) {
-                mIconResId = resId;
-                return this;
+                return mProvider.setIconResId_impl(resId);
             }
 
             public Builder setDisplayName(String displayName) {
-                mDisplayName = displayName;
-                return this;
+                return mProvider.setDisplayName_impl(displayName);
             }
 
             public Builder setEnabled(boolean enabled) {
-                mEnabled = enabled;
-                return this;
+                return mProvider.setEnabled_impl(enabled);
             }
 
             public Builder setExtra(Bundle extra) {
-                mExtra = extra;
-                return this;
+                return mProvider.setExtra_impl(extra);
             }
 
             public CommandButton build() {
-                if (mEnabled && mCommand == null) {
-                    throw new IllegalStateException("Enabled button needs Command"
-                            + " for controller to invoke the command");
-                }
-                if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
-                        && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
-                    throw new IllegalStateException("Custom commands needs icon and"
-                            + " and name to display");
-                }
-                return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
+                return mProvider.build_impl();
             }
         }
     }
@@ -795,7 +876,7 @@
     /**
      * Parameter for the playlist.
      */
-    public static class PlaylistParams {
+    public final static class PlaylistParams {
         /**
          * @hide
          */
@@ -850,79 +931,71 @@
          */
         public static final int SHUFFLE_MODE_GROUP = 2;
 
+
+        private final MediaSession2Provider.PlaylistParamsProvider mProvider;
+
         /**
-         * Keys used for converting a PlaylistParams object to a bundle object and vice versa.
+         * Instantiate {@link PlaylistParams}
+         *
+         * @param context context
+         * @param repeatMode repeat mode
+         * @param shuffleMode shuffle mode
+         * @param playlistMetadata metadata for the list
          */
-        private static final String KEY_REPEAT_MODE =
-                "android.media.session2.playlistparams2.repeat_mode";
-        private static final String KEY_SHUFFLE_MODE =
-                "android.media.session2.playlistparams2.shuffle_mode";
-        private static final String KEY_MEDIA_METADATA2_BUNDLE =
-                "android.media.session2.playlistparams2.metadata2_bundle";
-
-        private @RepeatMode int mRepeatMode;
-        private @ShuffleMode int mShuffleMode;
-
-        private MediaMetadata2 mPlaylistMetadata;
-
-        public PlaylistParams(@RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
-                @Nullable MediaMetadata2 playlistMetadata) {
-            mRepeatMode = repeatMode;
-            mShuffleMode = shuffleMode;
-            mPlaylistMetadata = playlistMetadata;
+        public PlaylistParams(@NonNull Context context, @RepeatMode int repeatMode,
+                @ShuffleMode int shuffleMode, @Nullable MediaMetadata2 playlistMetadata) {
+            mProvider = ApiLoader.getProvider(context).createMediaSession2PlaylistParams(
+                    context, this, repeatMode, shuffleMode, playlistMetadata);
         }
 
+        /**
+         * Create a new bundle for this object.
+         *
+         * @return
+         */
+        public @NonNull Bundle toBundle() {
+            return mProvider.toBundle_impl();
+        }
+
+        /**
+         * Create a new playlist params from the bundle that was previously returned by
+         * {@link #toBundle}.
+         *
+         * @param context context
+         * @return a new playlist params. Can be {@code null} for error.
+         */
+        public static @Nullable PlaylistParams fromBundle(
+                @NonNull Context context, @Nullable Bundle bundle) {
+            return ApiLoader.getProvider(context).fromBundle_PlaylistParams(context, bundle);
+        }
+
+        /**
+         * Get repeat mode
+         *
+         * @return repeat mode
+         * @see #REPEAT_MODE_NONE, #REPEAT_MODE_ONE, #REPEAT_MODE_ALL, #REPEAT_MODE_GROUP
+         */
         public @RepeatMode int getRepeatMode() {
-            return mRepeatMode;
+            return mProvider.getRepeatMode_impl();
         }
 
+        /**
+         * Get shuffle mode
+         *
+         * @return shuffle mode
+         * @see #SHUFFLE_MODE_NONE, #SHUFFLE_MODE_ALL, #SHUFFLE_MODE_GROUP
+         */
         public @ShuffleMode int getShuffleMode() {
-            return mShuffleMode;
-        }
-
-        public MediaMetadata2 getPlaylistMetadata() {
-            return mPlaylistMetadata;
+            return mProvider.getShuffleMode_impl();
         }
 
         /**
-         * Returns this object as a bundle to share between processes.
+         * Get metadata for the playlist
          *
-         * @hide
+         * @return metadata. Can be {@code null}
          */
-        public Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_REPEAT_MODE, mRepeatMode);
-            bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode);
-            if (mPlaylistMetadata != null) {
-                bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.getBundle());
-            }
-            return bundle;
-        }
-
-        /**
-         * Creates an instance from a bundle which is previously created by {@link #toBundle()}.
-         *
-         * @param bundle A bundle created by {@link #toBundle()}.
-         * @return A new {@link PlaylistParams} instance. Returns {@code null} if the given
-         *         {@param bundle} is null, or if the {@param bundle} has no playlist parameters.
-         * @hide
-         */
-        public static PlaylistParams fromBundle(Bundle bundle) {
-            if (bundle == null) {
-                return null;
-            }
-            if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) {
-                return null;
-            }
-
-            Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE);
-            MediaMetadata2 metadata =
-                    metadataBundle == null ? null : new MediaMetadata2(metadataBundle);
-
-            return new PlaylistParams(
-                    bundle.getInt(KEY_REPEAT_MODE),
-                    bundle.getInt(KEY_SHUFFLE_MODE),
-                    metadata);
+        public @Nullable MediaMetadata2 getPlaylistMetadata() {
+            return mProvider.getPlaylistMetadata_impl();
         }
     }
 
@@ -940,23 +1013,15 @@
      *       framework had to add heuristics to figure out if an app is
      * @hide
      */
-    MediaSession2(Context context, MediaPlayerInterface player, String id,
-            VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
-            Executor callbackExecutor, SessionCallback callback) {
+    @SystemApi
+    public MediaSession2(MediaSession2Provider provider) {
         super();
-        mProvider = createProvider(context, player, id, volumeProvider, ratingType, sessionActivity,
-                callbackExecutor, callback);
-        mProvider.initialize();
+        mProvider = provider;
     }
 
-    MediaSession2Provider createProvider(Context context, MediaPlayerInterface player, String id,
-            VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
-            Executor callbackExecutor, SessionCallback callback) {
-        return ApiLoader.getProvider(context)
-                .createMediaSession2(context, this, player, id, volumeProvider, ratingType,
-                        sessionActivity, callbackExecutor, callback);
-    }
-
+    /**
+     * @hide
+     */
     @SystemApi
     public MediaSession2Provider getProvider() {
         return mProvider;
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 6b2de06..0b5dddf 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,10 +21,12 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
 import android.media.update.MediaSessionService2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.os.IBinder;
 
 /**
@@ -165,7 +167,6 @@
      * @param state playback state
      * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
      */
-    // TODO(jaewan): Also add metadata
     public MediaNotification onUpdateNotification(PlaybackState2 state) {
         return mProvider.onUpdateNotification_impl(state);
     }
@@ -204,31 +205,31 @@
      * foreground service to keep playback running in the background. It's highly recommended to
      * show media style notification here.
      */
-    // TODO(jaewan): Should we also move this to updatable?
     public static class MediaNotification {
-        public final int id;
-        public final Notification notification;
-
-        private MediaNotification(int id, @NonNull Notification notification) {
-            this.id = id;
-            this.notification = notification;
-        }
+        private final MediaNotificationProvider mProvider;
 
         /**
-         * Create a {@link MediaNotification}.
+         * Default constructor
          *
+         * @param context context
          * @param notificationId notification id to be used for
          *      {@link android.app.NotificationManager#notify(int, Notification)}.
          * @param notification a notification to make session service foreground service. Media
          *      style notification is recommended here.
-         * @return
          */
-        public static MediaNotification create(int notificationId,
-                @NonNull Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("Notification cannot be null");
-            }
-            return new MediaNotification(notificationId, notification);
+        public MediaNotification(@NonNull Context context,
+                int notificationId, @NonNull Notification notification) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSessionService2MediaNotification(
+                            context, this, notificationId, notification);
+        }
+
+        public int getNotificationId() {
+            return mProvider.getNotificationId_impl();
+        }
+
+        public Notification getNotification() {
+            return mProvider.getNotification_impl();
         }
     }
 }
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
new file mode 100644
index 0000000..21f9171
--- /dev/null
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.util.Pair;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Class providing information on a microphone. It indicates the location and orientation of the
+ * microphone on the device as well as useful information like frequency response and sensitivity.
+ * It can be used by applications implementing special pre processing effects like noise suppression
+ * of beam forming that need to know about precise microphone characteristics in order to adapt
+ * their algorithms.
+ */
+public final class MicrophoneInfo {
+
+    /**
+     * A microphone that the location is unknown.
+     */
+    public static final int LOCATION_UNKNOWN = 0;
+
+    /**
+     * A microphone that locate on main body of the device.
+     */
+    public static final int LOCATION_MAINBODY = 1;
+
+    /**
+     * A microphone that locate on a movable main body of the device.
+     */
+    public static final int LOCATION_MAINBODY_MOVABLE = 2;
+
+    /**
+     * A microphone that locate on a peripheral.
+     */
+    public static final int LOCATION_PERIPHERAL = 3;
+
+    /**
+     * Unknown microphone directionality.
+     */
+    public static final int DIRECTIONALITY_UNKNOW = 0;
+
+    /**
+     * Microphone directionality type: omni.
+     */
+    public static final int DIRECTIONALITY_OMNI = 1;
+
+    /**
+     * Microphone directionality type: bi-directional.
+     */
+    public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2;
+
+    /**
+     * Microphone directionality type: cardioid.
+     */
+    public static final int DIRECTIONALITY_CARDIOID = 3;
+
+    /**
+     * Microphone directionality type: hyper cardioid.
+     */
+    public static final int DIRECTIONALITY_HYPER_CARDIOID = 4;
+
+    /**
+     * Microphone directionality type: super cardioid.
+     */
+    public static final int DIRECTIONALITY_SUPER_CARDIOID = 5;
+
+    /**
+     * The channel contains raw audio from this microphone.
+     */
+    public static final int CHANNEL_MAPPING_DIRECT = 1;
+
+    /**
+     * The channel contains processed audio from this microphone and possibly another microphone.
+     */
+    public static final int CHANNEL_MAPPING_PROCESSED = 2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "LOCATION_" }, value = {
+            LOCATION_UNKNOWN,
+            LOCATION_MAINBODY,
+            LOCATION_MAINBODY_MOVABLE,
+            LOCATION_PERIPHERAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MicrophoneLocation {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = {
+            DIRECTIONALITY_UNKNOW,
+            DIRECTIONALITY_OMNI,
+            DIRECTIONALITY_BI_DIRECTIONAL,
+            DIRECTIONALITY_CARDIOID,
+            DIRECTIONALITY_HYPER_CARDIOID,
+            DIRECTIONALITY_SUPER_CARDIOID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MicrophoneDirectionality {}
+
+    private Coordinate3F mPosition;
+    private Coordinate3F mOrientation;
+    private String mDeviceId;
+    private String mAddress;
+    private List<Pair<Float, Float>> mFrequencyResponse;
+    private List<Pair<Integer, Integer>> mChannelMapping;
+    private float mMaxSpl;
+    private float mMinSpl;
+    private float mSensitivity;
+    private int mLocation;
+    private int mGroup; /* Usually 0 will be used for main body. */
+    private int mIndexInTheGroup;
+    private int mPortId; /* mPortId will correspond to the id in AudioPort */
+    private int mType;
+    private int mDirectionality;
+
+    MicrophoneInfo(String deviceId, int type, String address, int location,
+            int group, int indexInTheGroup, Coordinate3F position,
+            Coordinate3F orientation, List<Pair<Float, Float>> frequencyResponse,
+            List<Pair<Integer, Integer>> channelMapping, float sensitivity, float maxSpl,
+            float minSpl, int directionality) {
+        mDeviceId = deviceId;
+        mType = type;
+        mAddress = address;
+        mLocation = location;
+        mGroup = group;
+        mIndexInTheGroup = indexInTheGroup;
+        mPosition = position;
+        mOrientation = orientation;
+        mFrequencyResponse = frequencyResponse;
+        mChannelMapping = channelMapping;
+        mSensitivity = sensitivity;
+        mMaxSpl = maxSpl;
+        mMinSpl = minSpl;
+        mDirectionality = directionality;
+    }
+
+    /**
+     * Returns alphanumeric code that uniquely identifies the device.
+     *
+     * @return the description of the microphone
+     */
+    public String getDescription() {
+        return mDeviceId;
+    }
+
+    /**
+     * Returns The system unique device ID that corresponds to the id
+     * returned by {@link AudioDeviceInfo#getId()}.
+     *
+     * @return the microphone's id
+     */
+    public int getId() {
+        return mPortId;
+    }
+
+    /**
+     * @hide
+     * Returns the internal device type (e.g AudioSystem.DEVICE_IN_BUILTIN_MIC).
+     * The internal device type could be used when getting microphone's port id
+     * by matching type and address.
+     *
+     * @return the internal device type
+     */
+    public int getInternalDeviceType() {
+        return mType;
+    }
+
+    /**
+     * Returns the device type identifier of the microphone (e.g AudioDeviceInfo.TYPE_BUILTIN_MIC).
+     *
+     * @return the device type of the microphone
+     */
+    public int getType() {
+        return AudioDeviceInfo.convertInternalDeviceToDeviceType(mType);
+    }
+
+    /**
+     * @hide
+     * Returns The "address" string of the microphone that corresponds to the
+     * address returned by {@link AudioDeviceInfo#getAddress()}
+     * @return the address of the microphone
+     */
+    public String getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Returns the location of the microphone. The return value is
+     * one of {@link #LOCATION_UNKNOWN}, {@link #LOCATION_MAINBODY},
+     * {@link #LOCATION_MAINBODY_MOVABLE}, or {@link #LOCATION_PERIPHERAL}.
+     *
+     * @return the location of the microphone
+     */
+    public @MicrophoneLocation int getLocation() {
+        return mLocation;
+    }
+
+    /**
+     * Returns A device group id that can be used to group together microphones on the same
+     * peripheral, attachments or logical groups. Main body is usually group 0.
+     *
+     * @return the group of the microphone
+     */
+    public int getGroup() {
+        return mGroup;
+    }
+
+    /**
+     * Returns unique index for device within its group.
+     *
+     * @return the microphone's index in its group
+     */
+    public int getIndexInTheGroup() {
+        return mIndexInTheGroup;
+    }
+
+    /**
+     * Returns A {@link Coordinate3F} object that represents the geometric location of microphone
+     * in meters, from botton-left-back corner of appliance. X-axis, Y-axis and Z-axis show
+     * as the x, y, z values.
+     *
+     * @return the geometric location of the microphone
+     */
+    public Coordinate3F getPosition() {
+        return mPosition;
+    }
+
+    /**
+     * Returns A {@link Coordinate3F} object that represents the orientation of microphone.
+     * X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized
+     * such as sqrt(x^2 + y^2 + z^2) equals 1.
+     *
+     * @return the orientation of the microphone
+     */
+    public Coordinate3F getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * Returns a {@link android.util.Pair} list of frequency responses.
+     * For every {@link android.util.Pair} in the list, the first value represents frequency in Hz,
+     * and the second value represents response in dB.
+     *
+     * @return the frequency response of the microphone
+     */
+    public List<Pair<Float, Float>> getFrequencyResponse() {
+        return mFrequencyResponse;
+    }
+
+    /**
+     * Returns a {@link android.util.Pair} list for channel mapping, which indicating how this
+     * microphone is used by each channels or a capture stream. For each {@link android.util.Pair},
+     * the first value is channel index, the second value is channel mapping type, which could be
+     * either {@link #CHANNEL_MAPPING_DIRECT} or {@link #CHANNEL_MAPPING_PROCESSED}.
+     * If a channel has contributions from more than one microphone, it is likely the HAL
+     * did some extra processing to combine the sources, but this is to be inferred by the user.
+     * Empty list when the MicrophoneInfo is returned by AudioManager.getMicrophones().
+     * At least one entry when the MicrophoneInfo is returned by AudioRecord.getActiveMicrophones().
+     *
+     * @return a {@link android.util.Pair} list for channel mapping
+     */
+    public List<Pair<Integer, Integer>> getChannelMapping() {
+        return mChannelMapping;
+    }
+
+    /**
+     * Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL.
+     *
+     * @return the sensitivity of the microphone
+     */
+    public float getSensitivity() {
+        return mSensitivity;
+    }
+
+    /**
+     * Returns the level in dB of the maximum SPL supported by the device at 1000Hz.
+     *
+     * @return the maximum level in dB
+     */
+    public float getMaxSpl() {
+        return mMaxSpl;
+    }
+
+    /**
+     * Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz.
+     *
+     * @return the minimum level in dB
+     */
+    public float getMinSpl() {
+        return mMinSpl;
+    }
+
+    /**
+     * Returns the directionality of microphone. The return value is one of
+     * {@link #DIRECTIONALITY_UNKNOW}, {@link #DIRECTIONALITY_OMNI},
+     * {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID},
+     * {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}.
+     *
+     * @return the directionality of microphone
+     */
+    public @MicrophoneDirectionality int getDirectionality() {
+        return mDirectionality;
+    }
+
+    /**
+     * Set the port id for the device.
+     * @hide
+     */
+    public void setId(int portId) {
+        mPortId = portId;
+    }
+
+    /* A class containing three float value to represent a 3D coordinate */
+    public class Coordinate3F {
+        public final float x;
+        public final float y;
+        public final float z;
+
+        Coordinate3F(float x, float y, float z) {
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+    }
+}
diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java
index da776eb..627974a 100644
--- a/media/java/android/media/PlaybackState2.java
+++ b/media/java/android/media/PlaybackState2.java
@@ -17,6 +17,11 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.PlaybackState2Provider;
 import android.os.Bundle;
 
 import java.lang.annotation.Retention;
@@ -29,14 +34,51 @@
  * @hide
  */
 public final class PlaybackState2 {
-    private static final String TAG = "PlaybackState2";
-
+    // Similar to the PlaybackState2 with following changes
+    //    - Not implement Parcelable and added from/toBundle()
+    //    - Removed playback state that doesn't match with the MediaPlayer2
+    //      Full list should be finalized when the MediaPlayer2 has getter for the playback state.
+    //      Here's table for the MP2 state and PlaybackState2.State.
+    //         +----------------------------------------+----------------------------------------+
+    //         | MediaPlayer2 state                     | Matching PlaybackState2.State          |
+    //         | (Names are from MP2' Javadoc)          |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Idle: Just finished creating MP2       | STATE_NONE                             |
+    //         |     or reset() is called               |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Initialized: setDataSource/Playlist    | N/A (Session/Controller don't          |
+    //         |                                        |     differentiate with Prepared)       |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Prepared: Prepared after initialized   | STATE_PAUSED                           |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Started: Started playback              | STATE_PLAYING                          |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Paused: Paused playback                | STATE_PAUSED                           |
+    //         +----------------------------------------+----------------------------------------+
+    //         | PlaybackCompleted: Playback is done    | STATE_PAUSED                           |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Stopped: MP2.stop() is called.         | STATE_STOPPED                          |
+    //         |     prepare() is needed to play again  |                                        |
+    //         |     (Seemingly the same as initialized |                                        |
+    //         |     because cannot set data source     |                                        |
+    //         |     after this)                        |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Error: an API is called in a state     | STATE_ERROR                            |
+    //         |     that the API isn't supported       |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //         | End: MP2.close() is called to release  | N/A (MediaSession will be gone)        |
+    //         |    MP2. Cannot be reused anymore       |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //         | Started, but                           | STATE_BUFFERING                        |
+    //         |    EventCallback.onBufferingUpdate()   |                                        |
+    //         +----------------------------------------+----------------------------------------+
+    //    - Removed actions and custom actions.
+    //    - Repeat mode / shuffle mode is now in the PlaylistParams
     // TODO(jaewan): Replace states from MediaPlayer2
     /**
      * @hide
      */
-    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PREPARED, STATE_PAUSED, STATE_PLAYING,
-            STATE_FINISH, STATE_BUFFERING, STATE_ERROR})
+    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_BUFFERING, STATE_ERROR})
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
@@ -52,88 +94,46 @@
     public final static int STATE_STOPPED = 1;
 
     /**
-     * State indicating this item is currently prepared
-     */
-    public final static int STATE_PREPARED = 2;
-
-    /**
      * State indicating this item is currently paused.
      */
-    public final static int STATE_PAUSED = 3;
+    public final static int STATE_PAUSED = 2;
 
     /**
      * State indicating this item is currently playing.
      */
-    public final static int STATE_PLAYING = 4;
-
-    /**
-     * State indicating the playback reaches the end of the item.
-     */
-    public final static int STATE_FINISH = 5;
+    public final static int STATE_PLAYING = 3;
 
     /**
      * State indicating this item is currently buffering and will begin playing
      * when enough data has buffered.
      */
-    public final static int STATE_BUFFERING = 6;
+    public final static int STATE_BUFFERING = 4;
 
     /**
      * State indicating this item is currently in an error state. The error
      * message should also be set when entering this state.
      */
-    public final static int STATE_ERROR = 7;
+    public final static int STATE_ERROR = 5;
 
     /**
      * Use this value for the position to indicate the position is not known.
      */
     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
 
-    /**
-     * Keys used for converting a PlaybackState2 to a bundle object and vice versa.
-     */
-    private static final String KEY_STATE = "android.media.playbackstate2.state";
-    private static final String KEY_POSITION = "android.media.playbackstate2.position";
-    private static final String KEY_BUFFERED_POSITION =
-            "android.media.playbackstate2.buffered_position";
-    private static final String KEY_SPEED = "android.media.playbackstate2.speed";
-    private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
-    private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
-    private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
-
-    private final int mState;
-    private final long mPosition;
-    private final long mUpdateTime;
-    private final float mSpeed;
-    private final long mBufferedPosition;
-    private final long mActiveItemId;
-    private final CharSequence mErrorMessage;
+    private final PlaybackState2Provider mProvider;
 
     // TODO(jaewan): Better error handling?
     //               E.g. media item at #2 has issue, but continue playing #3
     //                    login error. fire intent xxx to login
-    public PlaybackState2(int state, long position, long updateTime, float speed,
-            long bufferedPosition, long activeItemId, CharSequence error) {
-        mState = state;
-        mPosition = position;
-        mSpeed = speed;
-        mUpdateTime = updateTime;
-        mBufferedPosition = bufferedPosition;
-        mActiveItemId = activeItemId;
-        mErrorMessage = error;
+    public PlaybackState2(@NonNull Context context, int state, long position, long updateTime,
+            float speed, long bufferedPosition, long activeItemId, CharSequence error) {
+        mProvider = ApiLoader.getProvider(context).createPlaybackState2(context, this, state,
+                position, updateTime, speed, bufferedPosition, activeItemId, error);
     }
 
     @Override
     public String toString() {
-        StringBuilder bob = new StringBuilder("PlaybackState {");
-        bob.append("state=").append(mState);
-        bob.append(", position=").append(mPosition);
-        bob.append(", buffered position=").append(mBufferedPosition);
-        bob.append(", speed=").append(mSpeed);
-        bob.append(", updated=").append(mUpdateTime);
-        bob.append(", active item id=").append(mActiveItemId);
-        bob.append(", error=").append(mErrorMessage);
-        bob.append("}");
-        return bob.toString();
+        return mProvider.toString_impl();
     }
 
     /**
@@ -141,22 +141,24 @@
      * <ul>
      * <li> {@link PlaybackState2#STATE_NONE}</li>
      * <li> {@link PlaybackState2#STATE_STOPPED}</li>
-     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
+     * <li> {@link PlaybackState2#STATE_PREPARED}</li>
      * <li> {@link PlaybackState2#STATE_PAUSED}</li>
+     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
+     * <li> {@link PlaybackState2#STATE_FINISH}</li>
      * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
      * <li> {@link PlaybackState2#STATE_ERROR}</li>
      * </ul>
      */
     @State
     public int getState() {
-        return mState;
+        return mProvider.getState_impl();
     }
 
     /**
      * Get the current playback position in ms.
      */
     public long getPosition() {
-        return mPosition;
+        return mProvider.getPosition_impl();
     }
 
     /**
@@ -165,7 +167,7 @@
      * content.
      */
     public long getBufferedPosition() {
-        return mBufferedPosition;
+        return mProvider.getBufferedPosition_impl();
     }
 
     /**
@@ -176,7 +178,7 @@
      * @return The current speed of playback.
      */
     public float getPlaybackSpeed() {
-        return mSpeed;
+        return mProvider.getPlaybackSpeed_impl();
     }
 
     /**
@@ -184,7 +186,7 @@
      * {@link PlaybackState2#STATE_ERROR}.
      */
     public CharSequence getErrorMessage() {
-        return mErrorMessage;
+        return mProvider.getErrorMessage_impl();
     }
 
     /**
@@ -194,7 +196,7 @@
      * @return The last time the position was updated.
      */
     public long getLastPositionUpdateTime() {
-        return mUpdateTime;
+        return mProvider.getLastPositionUpdateTime_impl();
     }
 
     /**
@@ -203,53 +205,26 @@
      * @return The id of the currently active item in the queue
      */
     public long getCurrentPlaylistItemIndex() {
-        return mActiveItemId;
+        return mProvider.getCurrentPlaylistItemIndex_impl();
     }
 
     /**
      * Returns this object as a bundle to share between processes.
      */
-    public Bundle toBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STATE, mState);
-        bundle.putLong(KEY_POSITION, mPosition);
-        bundle.putLong(KEY_UPDATE_TIME, mUpdateTime);
-        bundle.putFloat(KEY_SPEED, mSpeed);
-        bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
-        bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
-        bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
-        return bundle;
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
     }
 
     /**
      * Creates an instance from a bundle which is previously created by {@link #toBundle()}.
      *
+     * @param context context
      * @param bundle A bundle created by {@link #toBundle()}.
      * @return A new {@link PlaybackState2} instance. Returns {@code null} if the given
      *         {@param bundle} is null, or if the {@param bundle} has no playback state parameters.
      */
-    public static PlaybackState2 fromBundle(Bundle bundle) {
-        if (bundle == null) {
-            return null;
-        }
-
-        if (!bundle.containsKey(KEY_STATE)
-                || !bundle.containsKey(KEY_POSITION)
-                || !bundle.containsKey(KEY_UPDATE_TIME)
-                || !bundle.containsKey(KEY_SPEED)
-                || !bundle.containsKey(KEY_BUFFERED_POSITION)
-                || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
-                || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
-            return null;
-        }
-
-        return new PlaybackState2(
-                bundle.getInt(KEY_STATE),
-                bundle.getLong(KEY_POSITION),
-                bundle.getLong(KEY_UPDATE_TIME),
-                bundle.getFloat(KEY_SPEED),
-                bundle.getLong(KEY_BUFFERED_POSITION),
-                bundle.getLong(KEY_ACTIVE_ITEM_ID),
-                bundle.getCharSequence(KEY_ERROR_MESSAGE));
+    public @Nullable static PlaybackState2 fromBundle(@NonNull Context context,
+            @Nullable Bundle bundle) {
+        return ApiLoader.getProvider(context).fromBundle_PlaybackState2(context, bundle);
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 93aea6f..4f77ecd 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -16,7 +16,13 @@
 
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.Rating2Provider;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -33,10 +39,8 @@
  * @hide
  */
 public final class Rating2 {
-    private static final String TAG = "Rating2";
-
-    private static final String KEY_STYLE = "android.media.rating2.style";
-    private static final String KEY_VALUE = "android.media.rating2.value";
+    // Mostly same as the android.media.Rating, but it's no longer implements Parcelable for
+    // updatable support.
 
     /**
      * @hide
@@ -91,31 +95,48 @@
      */
     public final static int RATING_PERCENTAGE = 6;
 
-    private final static float RATING_NOT_RATED = -1.0f;
+    private final Rating2Provider mProvider;
 
-    private final int mRatingStyle;
-
-    private final float mRatingValue;
-
-    private Rating2(@Style int ratingStyle, float rating) {
-        mRatingStyle = ratingStyle;
-        mRatingValue = rating;
+    /**
+     * @hide
+     */
+    @SystemApi
+    public Rating2(@NonNull Rating2Provider provider) {
+        mProvider = provider;
     }
 
     @Override
     public String toString() {
-        return "Rating2:style=" + mRatingStyle + " rating="
-                + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
+        return mProvider.toString_impl();
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public Rating2Provider getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return mProvider.equals_impl(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return mProvider.hashCode_impl();
     }
 
     /**
      * Create an instance from bundle object, previoulsy created by {@link #toBundle()}
      *
+     * @param context context
      * @param bundle bundle
-     * @return new Rating2 instance
+     * @return new Rating2 instance or {@code null} for error
      */
-    public static Rating2 fromBundle(Bundle bundle) {
-        return new Rating2(bundle.getInt(KEY_STYLE), bundle.getFloat(KEY_VALUE));
+    public static Rating2 fromBundle(@NonNull Context context, @Nullable Bundle bundle) {
+        return ApiLoader.getProvider(context).fromBundle_Rating2(context, bundle);
     }
 
     /**
@@ -123,55 +144,45 @@
      * @return bundle of this object
      */
     public Bundle toBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STYLE, mRatingStyle);
-        bundle.putFloat(KEY_VALUE, mRatingValue);
-        return bundle;
+        return mProvider.toBundle_impl();
     }
 
     /**
      * Return a Rating2 instance with no rating.
      * Create and return a new Rating2 instance with no rating known for the given
      * rating style.
+     * @param context context
      * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
      *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
      *    or {@link #RATING_PERCENTAGE}.
      * @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
      */
-    public static Rating2 newUnratedRating(@Style int ratingStyle) {
-        switch(ratingStyle) {
-            case RATING_HEART:
-            case RATING_THUMB_UP_DOWN:
-            case RATING_3_STARS:
-            case RATING_4_STARS:
-            case RATING_5_STARS:
-            case RATING_PERCENTAGE:
-                return new Rating2(ratingStyle, RATING_NOT_RATED);
-            default:
-                return null;
-        }
+    public static @Nullable Rating2 newUnratedRating(@NonNull Context context, @Style int ratingStyle) {
+        return ApiLoader.getProvider(context).newUnratedRating_Rating2(context, ratingStyle);
     }
 
     /**
      * Return a Rating2 instance with a heart-based rating.
      * Create and return a new Rating2 instance with a rating style of {@link #RATING_HEART},
      * and a heart-based rating.
+     * @param context context
      * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
      * @return a new Rating2 instance.
      */
-    public static Rating2 newHeartRating(boolean hasHeart) {
-        return new Rating2(RATING_HEART, hasHeart ? 1.0f : 0.0f);
+    public static @Nullable Rating2 newHeartRating(@NonNull Context context, boolean hasHeart) {
+        return ApiLoader.getProvider(context).newHeartRating_Rating2(context, hasHeart);
     }
 
     /**
      * Return a Rating2 instance with a thumb-based rating.
      * Create and return a new Rating2 instance with a {@link #RATING_THUMB_UP_DOWN}
      * rating style, and a "thumb up" or "thumb down" rating.
+     * @param context context
      * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
      * @return a new Rating2 instance.
      */
-    public static Rating2 newThumbRating(boolean thumbIsUp) {
-        return new Rating2(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
+    public static @Nullable Rating2 newThumbRating(@NonNull Context context, boolean thumbIsUp) {
+        return ApiLoader.getProvider(context).newThumbRating_Rating2(context, thumbIsUp);
     }
 
     /**
@@ -179,6 +190,7 @@
      * Create and return a new Rating2 instance with one of the star-base rating styles
      * and the given integer or fractional number of stars. Non integer values can for instance
      * be used to represent an average rating value, which might not be an integer number of stars.
+     * @param context context
      * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
      *     {@link #RATING_5_STARS}.
      * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
@@ -186,51 +198,30 @@
      * @return null if the rating style is invalid, or the rating is out of range,
      *     a new Rating2 instance otherwise.
      */
-    public static Rating2 newStarRating(@StarStyle int starRatingStyle, float starRating) {
-        float maxRating = -1.0f;
-        switch(starRatingStyle) {
-            case RATING_3_STARS:
-                maxRating = 3.0f;
-                break;
-            case RATING_4_STARS:
-                maxRating = 4.0f;
-                break;
-            case RATING_5_STARS:
-                maxRating = 5.0f;
-                break;
-            default:
-                Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
-                        return null;
-        }
-        if ((starRating < 0.0f) || (starRating > maxRating)) {
-            Log.e(TAG, "Trying to set out of range star-based rating");
-            return null;
-        }
-        return new Rating2(starRatingStyle, starRating);
+    public static @Nullable Rating2 newStarRating(@NonNull Context context,
+            @StarStyle int starRatingStyle, float starRating) {
+        return ApiLoader.getProvider(context).newStarRating_Rating2(
+                context, starRatingStyle, starRating);
     }
 
     /**
      * Return a Rating2 instance with a percentage-based rating.
      * Create and return a new Rating2 instance with a {@link #RATING_PERCENTAGE}
      * rating style, and a rating of the given percentage.
+     * @param context context
      * @param percent the value of the rating
      * @return null if the rating is out of range, a new Rating2 instance otherwise.
      */
-    public static Rating2 newPercentageRating(float percent) {
-        if ((percent < 0.0f) || (percent > 100.0f)) {
-            Log.e(TAG, "Invalid percentage-based rating value");
-            return null;
-        } else {
-            return new Rating2(RATING_PERCENTAGE, percent);
-        }
+    public static @Nullable Rating2 newPercentageRating(@NonNull Context context, float percent) {
+        return ApiLoader.getProvider(context).newPercentageRating_Rating2(context, percent);
     }
 
     /**
      * Return whether there is a rating value available.
-     * @return true if the instance was not created with {@link #newUnratedRating(int)}.
+     * @return true if the instance was not created with {@link #newUnratedRating(Context, int)}.
      */
     public boolean isRated() {
-        return mRatingValue >= 0.0f;
+        return mProvider.isRated_impl();
     }
 
     /**
@@ -241,7 +232,7 @@
      */
     @Style
     public int getRatingStyle() {
-        return mRatingStyle;
+        return mProvider.getRatingStyle_impl();
     }
 
     /**
@@ -250,11 +241,7 @@
      *    if the rating style is not {@link #RATING_HEART} or if it is unrated.
      */
     public boolean hasHeart() {
-        if (mRatingStyle != RATING_HEART) {
-            return false;
-        } else {
-            return (mRatingValue == 1.0f);
-        }
+        return mProvider.hasHeart_impl();
     }
 
     /**
@@ -263,11 +250,7 @@
      *    if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
      */
     public boolean isThumbUp() {
-        if (mRatingStyle != RATING_THUMB_UP_DOWN) {
-            return false;
-        } else {
-            return (mRatingValue == 1.0f);
-        }
+        return mProvider.isThumbUp_impl();
     }
 
     /**
@@ -276,16 +259,7 @@
      *    not star-based, or if it is unrated.
      */
     public float getStarRating() {
-        switch (mRatingStyle) {
-            case RATING_3_STARS:
-            case RATING_4_STARS:
-            case RATING_5_STARS:
-                if (isRated()) {
-                    return mRatingValue;
-                }
-            default:
-                return -1.0f;
-        }
+        return mProvider.getStarRating_impl();
     }
 
     /**
@@ -294,10 +268,6 @@
      *    not percentage-based, or if it is unrated.
      */
     public float getPercentRating() {
-        if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
-            return -1.0f;
-        } else {
-            return mRatingValue;
-        }
+        return mProvider.getPercentRating_impl();
     }
 }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 3eb9d52..fefa1ed 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -47,22 +49,17 @@
 
 import com.android.internal.database.SortCursor;
 
-import libcore.io.Streams;
-
 import java.io.Closeable;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
 /**
  * RingtoneManager provides access to ringtones, notification, and other types
  * of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@
             final Uri cacheUri = getCacheForType(type, context.getUserId());
             try (InputStream in = openRingtone(context, ringtoneUri);
                     OutputStream out = resolver.openOutputStream(cacheUri)) {
-                Streams.copy(in, out);
+                FileUtils.copy(in, out);
             } catch (IOException e) {
                 Log.w(TAG, "Failed to cache ringtone: " + e);
             }
@@ -960,7 +957,7 @@
         // Copy contents to external ringtone storage. Throws IOException if the copy fails.
         try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
                 final OutputStream output = new FileOutputStream(outFile)) {
-            Streams.copy(input, output);
+            FileUtils.copy(input, output);
         }
 
         // Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
new file mode 100644
index 0000000..00746e2
--- /dev/null
+++ b/media/java/android/media/VolumeProvider2.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.VolumeProvider2Provider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handles requests to adjust or set the volume on a session. This is also used
+ * to push volume updates back to the session. The provider must call
+ * {@link #setCurrentVolume(int)} each time the volume being provided changes.
+ * <p>
+ * You can set a volume provider on a session by calling
+ * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider)}.
+ *
+ * @hide
+ */
+public abstract class VolumeProvider2 {
+
+    /**
+     * @hide
+     */
+    @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ControlType {}
+
+    /**
+     * The volume is fixed and can not be modified. Requests to change volume
+     * should be ignored.
+     */
+    public static final int VOLUME_CONTROL_FIXED = 0;
+
+    /**
+     * The volume control uses relative adjustment via
+     * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
+     * value should be ignored.
+     */
+    public static final int VOLUME_CONTROL_RELATIVE = 1;
+
+    /**
+     * The volume control uses an absolute value. It may be adjusted using
+     * {@link #onAdjustVolume(int)} or set directly using
+     * {@link #onSetVolumeTo(int)}.
+     */
+    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
+
+    private final VolumeProvider2Provider mProvider;
+
+    /**
+     * Create a new volume provider for handling volume events. You must specify
+     * the type of volume control, the maximum volume that can be used, and the
+     * current volume on the output.
+     *
+     * @param controlType The method for controlling volume that is used by this provider.
+     * @param maxVolume The maximum allowed volume.
+     * @param currentVolume The current volume on the output.
+     */
+    public VolumeProvider2(@NonNull Context context, @ControlType int controlType,
+            int maxVolume, int currentVolume) {
+        mProvider = ApiLoader.getProvider(context).createVolumeProvider2(
+                context, this, controlType, maxVolume, currentVolume);
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public VolumeProvider2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Get the volume control type that this volume provider uses.
+     *
+     * @return The volume control type for this volume provider
+     */
+    @ControlType
+    public final int getControlType() {
+        return mProvider.getControlType_impl();
+    }
+
+    /**
+     * Get the maximum volume this provider allows.
+     *
+     * @return The max allowed volume.
+     */
+    public final int getMaxVolume() {
+        return mProvider.getMaxVolume_impl();
+    }
+
+    /**
+     * Gets the current volume. This will be the last value set by
+     * {@link #setCurrentVolume(int)}.
+     *
+     * @return The current volume.
+     */
+    public final int getCurrentVolume() {
+        return mProvider.getCurrentVolume_impl();
+    }
+
+    /**
+     * Notify the system that the current volume has been changed. This must be
+     * called every time the volume changes to ensure it is displayed properly.
+     *
+     * @param currentVolume The current volume on the output.
+     */
+    public final void setCurrentVolume(int currentVolume) {
+        mProvider.setCurrentVolume_impl(currentVolume);
+    }
+
+    /**
+     * Override to handle requests to set the volume of the current output.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param volume The volume to set the output to.
+     */
+    public void onSetVolumeTo(int volume) { }
+
+    /**
+     * Override to handle requests to adjust the volume of the current output.
+     * Direction will be one of {@link AudioManager#ADJUST_LOWER},
+     * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param direction The direction to change the volume in.
+     */
+    public void onAdjustVolume(int direction) { }
+}
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
index 67680c7..17256a8 100644
--- a/media/java/android/media/update/MediaBrowser2Provider.java
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -23,7 +23,7 @@
  * @hide
  */
 public interface MediaBrowser2Provider extends MediaController2Provider {
-    void getBrowserRoot_impl(Bundle rootHints);
+    void getLibraryRoot_impl(Bundle rootHints);
 
     void subscribe_impl(String parentId, Bundle options);
     void unsubscribe_impl(String parentId, Bundle options);
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 95fe363..9957a06 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.media.session.MediaController;
+import android.util.AttributeSet;
 import android.view.View;
 
 /**
@@ -34,12 +35,13 @@
  * @hide
  */
 // TODO @SystemApi
-public interface MediaControlView2Provider extends ViewProvider {
+public interface MediaControlView2Provider extends ViewGroupProvider {
+    void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
     void setController_impl(MediaController controller);
     boolean isShowing_impl();
     void setButtonVisibility_impl(int button, int visibility);
     void requestPlayButtonFocus_impl();
-    void onVisibilityAggregated_impl(boolean isVisible);
     void setTimeout_impl(long timeout);
     long getTimeout_impl();
 }
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index a568839..923551a 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -17,12 +17,15 @@
 package android.media.update;
 
 import android.annotation.SystemApi;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
 import android.media.MediaSession2.ControllerInfo;
 import android.os.Bundle;
 
 /**
  * @hide
  */
+// TODO: @SystemApi
 public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
     // Nothing new for now
 
@@ -30,4 +33,9 @@
         void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options);
         void notifyChildrenChanged_impl(String parentId, Bundle options);
     }
+
+    interface LibraryRootProvider {
+        String getRootId_impl();
+        Bundle getExtras_impl();
+    }
 }
diff --git a/media/java/android/media/update/MediaMetadata2Provider.java b/media/java/android/media/update/MediaMetadata2Provider.java
new file mode 100644
index 0000000..55ac43d
--- /dev/null
+++ b/media/java/android/media/update/MediaMetadata2Provider.java
@@ -0,0 +1,37 @@
+package android.media.update;
+
+import android.graphics.Bitmap;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.Builder;
+import android.media.Rating2;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+// TODO(jaewan): SystemApi
+public interface MediaMetadata2Provider {
+    boolean containsKey_impl(String key);
+    CharSequence getText_impl(String key);
+    String getMediaId_impl();
+    String getString_impl(String key);
+    long getLong_impl(String key);
+    Rating2 getRating_impl(String key);
+    Bundle toBundle_impl();
+    Set<String> keySet_impl();
+    int size_impl();
+    Bitmap getBitmap_impl(String key);
+    Bundle getExtra_impl();
+
+    interface BuilderProvider {
+        Builder putText_impl(String key, CharSequence value);
+        Builder putString_impl(String key, String value);
+        Builder putLong_impl(String key, long value);
+        Builder putRating_impl(String key, Rating2 value);
+        Builder putBitmap_impl(String key, Bitmap value);
+        Builder setExtra_impl(Bundle bundle);
+        MediaMetadata2 build_impl();
+    }
+}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index f9e29d9..9abf34a 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -16,14 +16,19 @@
 
 package android.media.update;
 
+import android.app.PendingIntent;
 import android.media.MediaItem2;
+import android.media.MediaMetadata2;
 import android.media.MediaPlayerInterface;
 import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.PlaylistParams;
+import android.media.MediaSession2.SessionCallback;
 import android.media.SessionToken2;
 import android.media.VolumeProvider;
 import android.os.Bundle;
@@ -37,8 +42,6 @@
  */
 // TODO: @SystemApi
 public interface MediaSession2Provider extends TransportControlProvider {
-    void initialize();
-
     void close_impl();
     void setPlayer_impl(MediaPlayerInterface player);
     void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider);
@@ -80,6 +83,23 @@
         Bundle toBundle_impl();
     }
 
+    interface CommandButtonProvider {
+        Command getCommand_impl();
+        int getIconResId_impl();
+        String getDisplayName_impl();
+        Bundle getExtra_impl();
+        boolean isEnabled_impl();
+
+        interface BuilderProvider {
+            Builder setCommand_impl(Command command);
+            Builder setIconResId_impl(int resId);
+            Builder setDisplayName_impl(String displayName);
+            Builder setEnabled_impl(boolean enabled);
+            Builder setExtra_impl(Bundle extra);
+            CommandButton build_impl();
+        }
+    }
+
     interface ControllerInfoProvider {
         String getPackageName_impl();
         int getUid_impl();
@@ -87,4 +107,20 @@
         int hashCode_impl();
         boolean equals_impl(ControllerInfoProvider obj);
     }
+
+    interface PlaylistParamsProvider {
+        int getRepeatMode_impl();
+        int getShuffleMode_impl();
+        MediaMetadata2 getPlaylistMetadata_impl();
+        Bundle toBundle_impl();
+    }
+
+    interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
+        void setVolumeProvider_impl(VolumeProvider volumeProvider);
+        void setRatingType_impl(int type);
+        void setSessionActivity_impl(PendingIntent pi);
+        void setId_impl(String id);
+        void setSessionCallback_impl(Executor executor, C callback);
+        T build_impl();
+    }
 }
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 9455da7..42e7587 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -17,6 +17,7 @@
 package android.media.update;
 
 import android.annotation.SystemApi;
+import android.app.Notification;
 import android.content.Intent;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2.MediaNotification;
@@ -33,4 +34,9 @@
     // Service
     void onCreate_impl();
     IBinder onBind_impl(Intent intent);
+
+    interface MediaNotificationProvider {
+        int getNotificationId_impl();
+        Notification getNotification_impl();
+    }
 }
diff --git a/media/java/android/media/update/PlaybackInfoProvider.java b/media/java/android/media/update/PlaybackInfoProvider.java
new file mode 100644
index 0000000..36eb58a
--- /dev/null
+++ b/media/java/android/media/update/PlaybackInfoProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.AudioAttributes;
+
+/**
+ * @hide
+ */
+// TODO(jaewan): @SystemApi
+public interface PlaybackInfoProvider {
+    int getPlaybackType_impl();
+    AudioAttributes getAudioAttributes_impl();
+    int getControlType_impl();
+    int getMaxVolume_impl();
+    int getCurrentVolume_impl();
+}
diff --git a/media/java/android/media/update/PlaybackState2Provider.java b/media/java/android/media/update/PlaybackState2Provider.java
new file mode 100644
index 0000000..2875e98
--- /dev/null
+++ b/media/java/android/media/update/PlaybackState2Provider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+// TODO(jaewan): @SystemApi
+public interface PlaybackState2Provider {
+    String toString_impl();
+
+    int getState_impl();
+
+    long getPosition_impl();
+
+    long getBufferedPosition_impl();
+
+    float getPlaybackSpeed_impl();
+
+    CharSequence getErrorMessage_impl();
+
+    long getLastPositionUpdateTime_impl();
+
+    long getCurrentPlaylistItemIndex_impl();
+
+    Bundle toBundle_impl();
+}
diff --git a/media/java/android/media/update/ProviderCreator.java b/media/java/android/media/update/ProviderCreator.java
new file mode 100644
index 0000000..f5f3e47
--- /dev/null
+++ b/media/java/android/media/update/ProviderCreator.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+/** @hide */
+@FunctionalInterface
+public interface ProviderCreator<T, U> {
+    U createProvider(T instance);
+}
diff --git a/media/java/android/media/update/Rating2Provider.java b/media/java/android/media/update/Rating2Provider.java
new file mode 100644
index 0000000..8966196
--- /dev/null
+++ b/media/java/android/media/update/Rating2Provider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+// TODO(jaewan): @SystemApi
+public interface Rating2Provider {
+    String toString_impl();
+    boolean equals_impl(Object obj);
+    int hashCode_impl();
+    Bundle toBundle_impl();
+    boolean isRated_impl();
+    int getRatingStyle_impl();
+    boolean hasHeart_impl();
+    boolean isThumbUp_impl();
+    float getStarRating_impl();
+    float getPercentRating_impl();
+}
\ No newline at end of file
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 9648b27..57f04cc 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,7 +17,7 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.app.PendingIntent;
+import android.app.Notification;
 import android.content.Context;
 import android.media.DataSourceDesc;
 import android.media.MediaBrowser2;
@@ -26,20 +26,31 @@
 import android.media.MediaController2.ControllerCallback;
 import android.media.MediaItem2;
 import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
 import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
+import android.media.MediaSession2.CommandButton.Builder;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.PlaybackState2;
+import android.media.Rating2;
 import android.media.SessionPlayer2;
 import android.media.SessionToken2;
-import android.media.VolumeProvider;
-import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
+import android.media.VolumeProvider2;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
 import android.media.update.MediaSession2Provider.CommandGroupProvider;
 import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.os.Bundle;
 import android.os.IInterface;
 import android.util.AttributeSet;
@@ -56,25 +67,29 @@
  * @hide
  */
 public interface StaticProvider {
-    MediaControlView2Provider createMediaControlView2(
-            MediaControlView2 instance, ViewProvider superProvider);
-    VideoView2Provider createVideoView2(
-            VideoView2 instance, ViewProvider superProvider,
+    MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
+            ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
+            @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+    VideoView2Provider createVideoView2(VideoView2 instance,
+            ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
 
-    MediaSession2Provider createMediaSession2(Context context, MediaSession2 instance,
-            MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType,
-            PendingIntent sessionActivity, Executor executor, SessionCallback callback);
     CommandProvider createMediaSession2Command(MediaSession2.Command instance,
             int commandCode, String action, Bundle extra);
     MediaSession2.Command fromBundle_MediaSession2Command(Context context, Bundle bundle);
     CommandGroupProvider createMediaSession2CommandGroup(Context context,
             MediaSession2.CommandGroup instance, MediaSession2.CommandGroup others);
     MediaSession2.CommandGroup fromBundle_MediaSession2CommandGroup(Context context, Bundle bundle);
-    ControllerInfoProvider createMediaSession2ControllerInfoProvider(Context context,
+    ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
             MediaSession2.ControllerInfo instance, int uid, int pid,
             String packageName, IInterface callback);
-
+    PlaylistParamsProvider createMediaSession2PlaylistParams(Context context,
+            PlaylistParams playlistParams, int repeatMode, int shuffleMode,
+            MediaMetadata2 playlistMetadata);
+    PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
+    BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
+    BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
+            Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
 
     MediaController2Provider createMediaController2(Context context, MediaController2 instance,
             SessionToken2 token, Executor executor, ControllerCallback callback);
@@ -83,12 +98,16 @@
             SessionToken2 token, Executor executor, BrowserCallback callback);
 
     MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
-    MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
+    MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+            MediaNotification mediaNotification, int notificationId, Notification notification);
 
-    MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(Context context,
-            MediaLibrarySession instance, MediaPlayerInterface player, String id,
-            VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
-            Executor executor, MediaLibrarySessionCallback callback);
+    MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
+    BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
+        createMediaLibraryService2Builder(
+            Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
+            Executor callbackExecutor, MediaLibrarySessionCallback callback);
+    LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
+            String rootId, Bundle extras);
 
     SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid);
@@ -96,7 +115,28 @@
 
     SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance);
 
-    MediaItem2Provider createMediaItem2Provider(Context context, MediaItem2 mediaItem2,
+    MediaItem2Provider createMediaItem2(Context context, MediaItem2 mediaItem2,
             String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags);
     MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle);
+
+    VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
+            int controlType, int maxVolume, int currentVolume);
+
+    MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle);
+    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            Context context, MediaMetadata2.Builder builder);
+    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            Context context, MediaMetadata2.Builder builder, MediaMetadata2 source);
+
+    Rating2 newUnratedRating_Rating2(Context context, int ratingStyle);
+    Rating2 fromBundle_Rating2(Context context, Bundle bundle);
+    Rating2 newHeartRating_Rating2(Context context, boolean hasHeart);
+    Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp);
+    Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating);
+    Rating2 newPercentageRating_Rating2(Context context, float percent);
+
+    PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance, int state,
+            long position, long updateTime, float speed, long bufferedPosition, long activeItemId,
+            CharSequence error);
+    PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle);
 }
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 10f03d2..c0fda87 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -22,6 +22,7 @@
 import android.media.session.PlaybackState;
 import android.media.session.MediaSession;
 import android.net.Uri;
+import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
@@ -43,7 +44,9 @@
  * @hide
  */
 // TODO @SystemApi
-public interface VideoView2Provider extends ViewProvider {
+public interface VideoView2Provider extends ViewGroupProvider {
+    void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
     void setMediaControlView2_impl(MediaControlView2 mediaControlView);
     MediaController getMediaController_impl();
     MediaControlView2 getMediaControlView2_impl();
diff --git a/media/java/android/media/update/ViewGroupHelper.java b/media/java/android/media/update/ViewGroupHelper.java
new file mode 100644
index 0000000..2c4f9b9
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupHelper.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewGroupProvider
+ *
+ * @hide
+ */
+public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
+    /** @hide */
+    final public T mProvider;
+
+    /** @hide */
+    public ViewGroupHelper(ProviderCreator<T> creator,
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mProvider = creator.createProvider(this, new SuperProvider(),
+                new PrivateProvider());
+    }
+
+    /** @hide */
+    // TODO @SystemApi
+    public T getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return mProvider.getAccessibilityClassName_impl();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mProvider.onTouchEvent_impl(ev);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent ev) {
+        return mProvider.onTrackballEvent_impl(ev);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        mProvider.onFinishInflate_impl();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mProvider.setEnabled_impl(enabled);
+    }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        mProvider.onVisibilityAggregated_impl(isVisible);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mProvider.onLayout_impl(changed, left, top, right, bottom);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        return mProvider.getSuggestedMinimumWidth_impl();
+    }
+
+    @Override
+    protected int getSuggestedMinimumHeight() {
+        return mProvider.getSuggestedMinimumHeight_impl();
+    }
+
+    // setMeasuredDimension is final
+
+    @Override
+    protected boolean checkLayoutParams(LayoutParams p) {
+        return mProvider.checkLayoutParams_impl(p);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return mProvider.generateDefaultLayoutParams_impl();
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return mProvider.generateLayoutParams_impl(attrs);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(LayoutParams lp) {
+        return mProvider.generateLayoutParams_impl(lp);
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return mProvider.shouldDelayChildPressedState_impl();
+    }
+
+    @Override
+    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed) {
+        mProvider.measureChildWithMargins_impl(child,
+                parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+    }
+
+    /** @hide */
+    public class SuperProvider implements ViewGroupProvider {
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return ViewGroupHelper.super.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.super.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.super.onTrackballEvent(ev);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            ViewGroupHelper.super.onFinishInflate();
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            ViewGroupHelper.super.setEnabled(enabled);
+        }
+
+        @Override
+        public void onAttachedToWindow_impl() {
+            ViewGroupHelper.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            ViewGroupHelper.super.onDetachedFromWindow();
+        }
+
+        @Override
+        public void onVisibilityAggregated_impl(boolean isVisible) {
+            ViewGroupHelper.super.onVisibilityAggregated(isVisible);
+        }
+
+        @Override
+        public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+            // abstract method; no super
+        }
+
+        @Override
+        public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+            ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public int getSuggestedMinimumWidth_impl() {
+            return ViewGroupHelper.super.getSuggestedMinimumWidth();
+        }
+
+        @Override
+        public int getSuggestedMinimumHeight_impl() {
+            return ViewGroupHelper.super.getSuggestedMinimumHeight();
+        }
+
+        @Override
+        public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+            ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        @Override
+        public boolean checkLayoutParams_impl(LayoutParams p) {
+            return ViewGroupHelper.super.checkLayoutParams(p);
+        }
+
+        @Override
+        public LayoutParams generateDefaultLayoutParams_impl() {
+            return ViewGroupHelper.super.generateDefaultLayoutParams();
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+            return ViewGroupHelper.super.generateLayoutParams(attrs);
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+            return ViewGroupHelper.super.generateLayoutParams(lp);
+        }
+
+        @Override
+        public boolean shouldDelayChildPressedState_impl() {
+            return ViewGroupHelper.super.shouldDelayChildPressedState();
+        }
+
+        @Override
+        public void measureChildWithMargins_impl(View child,
+                int parentWidthMeasureSpec, int widthUsed,
+                int parentHeightMeasureSpec, int heightUsed) {
+            ViewGroupHelper.super.measureChildWithMargins(child,
+                    parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+        }
+    }
+
+    /** @hide */
+    public class PrivateProvider implements ViewGroupProvider {
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return ViewGroupHelper.this.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.this.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.this.onTrackballEvent(ev);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            ViewGroupHelper.this.onFinishInflate();
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            ViewGroupHelper.this.setEnabled(enabled);
+        }
+
+        @Override
+        public void onAttachedToWindow_impl() {
+            ViewGroupHelper.this.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            ViewGroupHelper.this.onDetachedFromWindow();
+        }
+
+        @Override
+        public void onVisibilityAggregated_impl(boolean isVisible) {
+            ViewGroupHelper.this.onVisibilityAggregated(isVisible);
+        }
+
+        @Override
+        public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+            ViewGroupHelper.this.onLayout(changed, left, top, right, bottom);
+        }
+
+        @Override
+        public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+            ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public int getSuggestedMinimumWidth_impl() {
+            return ViewGroupHelper.this.getSuggestedMinimumWidth();
+        }
+
+        @Override
+        public int getSuggestedMinimumHeight_impl() {
+            return ViewGroupHelper.this.getSuggestedMinimumHeight();
+        }
+
+        @Override
+        public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+            ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        @Override
+        public boolean checkLayoutParams_impl(LayoutParams p) {
+            return ViewGroupHelper.this.checkLayoutParams(p);
+        }
+
+        @Override
+        public LayoutParams generateDefaultLayoutParams_impl() {
+            return ViewGroupHelper.this.generateDefaultLayoutParams();
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+            return ViewGroupHelper.this.generateLayoutParams(attrs);
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+            return ViewGroupHelper.this.generateLayoutParams(lp);
+        }
+
+        @Override
+        public boolean shouldDelayChildPressedState_impl() {
+            return ViewGroupHelper.this.shouldDelayChildPressedState();
+        }
+
+        @Override
+        public void measureChildWithMargins_impl(View child,
+                int parentWidthMeasureSpec, int widthUsed,
+                int parentHeightMeasureSpec, int heightUsed) {
+            ViewGroupHelper.this.measureChildWithMargins(child,
+                    parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+        }
+    }
+
+        /** @hide */
+    @FunctionalInterface
+    public interface ProviderCreator<T extends ViewGroupProvider> {
+        T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider,
+                ViewGroupProvider privateProvider);
+    }
+}
diff --git a/media/java/android/media/update/ViewGroupProvider.java b/media/java/android/media/update/ViewGroupProvider.java
new file mode 100644
index 0000000..5f12529
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.annotation.SystemApi;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.view.View
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface ViewGroupProvider {
+    // View methods
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
+    CharSequence getAccessibilityClassName_impl();
+    boolean onTouchEvent_impl(MotionEvent ev);
+    boolean onTrackballEvent_impl(MotionEvent ev);
+    void onFinishInflate_impl();
+    void setEnabled_impl(boolean enabled);
+    void onVisibilityAggregated_impl(boolean isVisible);
+    void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+    void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec);
+    int getSuggestedMinimumWidth_impl();
+    int getSuggestedMinimumHeight_impl();
+    void setMeasuredDimension_impl(int measuredWidth, int measuredHeight);
+
+    // ViewGroup methods
+    boolean checkLayoutParams_impl(LayoutParams p);
+    LayoutParams generateDefaultLayoutParams_impl();
+    LayoutParams generateLayoutParams_impl(AttributeSet attrs);
+    LayoutParams generateLayoutParams_impl(LayoutParams lp);
+    boolean shouldDelayChildPressedState_impl();
+    void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed,
+        int parentHeightMeasureSpec, int heightUsed);
+
+    // ViewManager methods
+    // ViewParent methods
+}
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
deleted file mode 100644
index 0dd8f38..0000000
--- a/media/java/android/media/update/ViewProvider.java
+++ /dev/null
@@ -1,45 +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.
- */
-
-package android.media.update;
-
-import android.annotation.SystemApi;
-import android.view.MotionEvent;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * Each instance object is connected to one corresponding updatable object which implements the
- * runtime behavior of that class. There should a corresponding provider method for all public
- * methods.
- *
- * All methods behave as per their namesake in the public API.
- *
- * @see android.view.View
- *
- * @hide
- */
-// TODO @SystemApi
-public interface ViewProvider {
-    // TODO Add more (all?) methods from View
-    void onAttachedToWindow_impl();
-    void onDetachedFromWindow_impl();
-    CharSequence getAccessibilityClassName_impl();
-    boolean onTouchEvent_impl(MotionEvent ev);
-    boolean onTrackballEvent_impl(MotionEvent ev);
-    void onFinishInflate_impl();
-    void setEnabled_impl(boolean enabled);
-}
diff --git a/media/java/android/media/update/VolumeProvider2Provider.java b/media/java/android/media/update/VolumeProvider2Provider.java
new file mode 100644
index 0000000..5657af6
--- /dev/null
+++ b/media/java/android/media/update/VolumeProvider2Provider.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.update;
+
+/**
+ * @hide
+ */
+// TODO(jaewan): @SystemApi
+public interface VolumeProvider2Provider {
+    int getControlType_impl();
+    int getMaxVolume_impl();
+    int getCurrentVolume_impl();
+    void setCurrentVolume_impl(int currentVolume);
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 051c802..7e5f581 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -34,6 +34,7 @@
         "libutils",
         "libbinder",
         "libmedia",
+        "libmediaextractor",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
new file mode 100644
index 0000000..71b8dacf
--- /dev/null
+++ b/media/jni/android_media_AudioPresentation.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+
+#include "jni.h"
+
+#include <media/AudioPresentationInfo.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+struct JAudioPresentationInfo {
+    struct fields_t {
+        jclass      clazz;
+        jmethodID   constructID;
+
+        // list parameters
+        jclass listclazz;
+        jmethodID listConstructId;
+        jmethodID listAddId;
+
+        void init(JNIEnv *env) {
+            jclass lclazz = env->FindClass("android/media/AudioPresentation");
+            if (lclazz == NULL) {
+                return;
+            }
+
+            clazz = (jclass)env->NewGlobalRef(lclazz);
+            if (clazz == NULL) {
+                return;
+            }
+
+            constructID = env->GetMethodID(clazz, "<init>",
+                                "(IILjava/util/Map;Ljava/lang/String;IZZZ)V");
+            env->DeleteLocalRef(lclazz);
+
+            // list objects
+            jclass llistclazz = env->FindClass("java/util/ArrayList");
+            CHECK(llistclazz != NULL);
+            listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
+            CHECK(listclazz != NULL);
+            listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+            CHECK(listConstructId != NULL);
+            listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+            CHECK(listAddId != NULL);
+            env->DeleteLocalRef(llistclazz);
+        }
+
+        void exit(JNIEnv *env) {
+            env->DeleteGlobalRef(clazz);
+            clazz = NULL;
+            env->DeleteGlobalRef(listclazz);
+            listclazz = NULL;
+        }
+    };
+
+    static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+        ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
+
+        if (hashMapClazz.get() == NULL) {
+            return -EINVAL;
+        }
+        jmethodID hashMapConstructID =
+            env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+        if (hashMapConstructID == NULL) {
+            return -EINVAL;
+        }
+        jmethodID hashMapPutID =
+            env->GetMethodID(
+                    hashMapClazz.get(),
+                    "put",
+                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+        if (hashMapPutID == NULL) {
+            return -EINVAL;
+        }
+
+        jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+        for (size_t i = 0; i < msg->countEntries(); ++i) {
+            AMessage::Type valueType;
+            const char *key = msg->getEntryNameAt(i, &valueType);
+
+            if (!strncmp(key, "android._", 9)) {
+                // don't expose private keys (starting with android._)
+                continue;
+            }
+
+            jobject valueObj = NULL;
+
+            AString val;
+            CHECK(msg->findString(key, &val));
+
+            valueObj = env->NewStringUTF(val.c_str());
+
+            if (valueObj != NULL) {
+                jstring keyObj = env->NewStringUTF(key);
+
+                env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+                env->DeleteLocalRef(keyObj); keyObj = NULL;
+                env->DeleteLocalRef(valueObj); valueObj = NULL;
+            }
+        }
+
+        *map = hashMap;
+
+        return OK;
+    }
+
+    jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
+        jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
+
+        for (size_t i = 0; i < info.countPresentations(); ++i) {
+            const sp<AudioPresentation> &ap = info.getPresentation(i);
+            jobject jLabelObject;
+
+            sp<AMessage> labelMessage = new AMessage();
+            for (size_t i = 0; i < ap->mLabels.size(); ++i) {
+                labelMessage->setString(ap->mLabels.keyAt(i).string(),
+                                        ap->mLabels.valueAt(i).string());
+            }
+            if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
+                return NULL;
+            }
+            jstring jLanguage = env->NewStringUTF(ap->mLanguage.string());
+
+            jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
+                                static_cast<jint>(ap->mPresentationId),
+                                static_cast<jint>(ap->mProgramId),
+                                jLabelObject,
+                                jLanguage,
+                                static_cast<jint>(ap->mMasteringIndication),
+                                static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
+                                    1 : 0),
+                                static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
+                                    1 : 0),
+                                static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
+                                    1 : 0));
+            if (jValueObj == NULL) {
+                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+                return NULL;
+            }
+
+            env->CallBooleanMethod(list, fields.listAddId, jValueObj);
+            env->DeleteLocalRef(jValueObj); jValueObj = NULL;
+            env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+        }
+        return list;
+    }
+};
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index d2bc174..b3a8b21 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -20,6 +20,7 @@
 #include <limits.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <vector>
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaRecorderJNI"
@@ -29,6 +30,7 @@
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
 #include <media/MediaAnalyticsItem.h>
+#include <media/MicrophoneInfo.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
 
@@ -36,7 +38,9 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include "android_media_AudioErrors.h"
 #include "android_media_MediaMetricsJNI.h"
+#include "android_media_MicrophoneInfo.h"
 #include "android_runtime/AndroidRuntime.h"
 
 #include <system/audio.h>
@@ -61,6 +65,12 @@
 };
 static fields_t fields;
 
+struct ArrayListFields {
+    jmethodID add;
+    jclass classId;
+};
+static ArrayListFields gArrayListFields;
+
 static Mutex sLock;
 
 // ----------------------------------------------------------------------------
@@ -565,6 +575,13 @@
     if (fields.post_event == NULL) {
         return;
     }
+
+    clazz = env->FindClass("java/util/ArrayList");
+    if (clazz == NULL) {
+        return;
+    }
+    gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
+    gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
 }
 
 
@@ -707,6 +724,45 @@
     process_media_recorder_call(env, mr->enableAudioDeviceCallback(enabled),
             "java/lang/RuntimeException", "enableDeviceCallback failed.");
 }
+
+static jint
+android_media_MediaRecord_getActiveMicrophones(JNIEnv *env,
+        jobject thiz, jobject jActiveMicrophones) {
+    if (jActiveMicrophones == NULL) {
+        ALOGE("jActiveMicrophones is null");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jActiveMicrophones, gArrayListFields.classId)) {
+        ALOGE("getActiveMicrophones not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    if (mr == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jint)AUDIO_JAVA_NO_INIT;
+    }
+
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    std::vector<media::MicrophoneInfo> activeMicrophones;
+    status_t status = mr->getActiveMicrophones(&activeMicrophones);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "MediaRecorder::getActiveMicrophones error %d", status);
+        jStatus = nativeToJavaStatus(status);
+        return jStatus;
+    }
+
+    for (size_t i = 0; i < activeMicrophones.size(); i++) {
+        jobject jMicrophoneInfo;
+        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jActiveMicrophones, gArrayListFields.add, jMicrophoneInfo);
+        env->DeleteLocalRef(jMicrophoneInfo);
+    }
+    return jStatus;
+}
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -742,7 +798,9 @@
 
     {"native_setInputDevice", "(I)Z",                           (void *)android_media_MediaRecorder_setInputDevice},
     {"native_getRoutedDeviceId", "()I",                         (void *)android_media_MediaRecorder_getRoutedDeviceId},
-    {"native_enableDeviceCallback", "(Z)V",                      (void *)android_media_MediaRecorder_enableDeviceCallback},
+    {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaRecorder_enableDeviceCallback},
+
+    {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
index 5680d9f..b3f443b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
@@ -1860,9 +1860,6 @@
             return new ArrayList<Integer>();
         }
 
-        checkArrayValuesInRange(key, availableCaps,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO);
         capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
         return capList;
     }
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 8b01aef..9f165bc 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
 import android.content.res.ObbScanner;
 import android.os.Binder;
 import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -43,7 +44,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import libcore.io.IoUtils;
-import libcore.io.Streams;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -260,7 +260,7 @@
             in = new FileInputStream(sourcePath);
             out = new ParcelFileDescriptor.AutoCloseOutputStream(
                     target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
-            Streams.copy(in, out);
+            FileUtils.copy(in, out);
         } finally {
             IoUtils.closeQuietly(out);
             IoUtils.closeQuietly(in);
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 6fe8975..9a66b07 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@
                 infile = mFile.openRead();
                 readXml(infile);
             } catch (FileNotFoundException e) {
-                // No data yet
+                Log.d(TAG, "File doesn't exist or isn't readable yet");
             } catch (IOException e) {
                 Log.e(TAG, "Unable to read channel impressions", e);
             } catch (NumberFormatException | XmlPullParserException e) {
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 7c35b48..db48f61 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -325,7 +325,8 @@
         int dismiss2 = 777;
         String key2 = mAssistant.getKey("pkg2", 2, "channel2");
 
-        String xml = "<assistant version=\"1\">\n"
+        String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<assistant version=\"1\">\n"
                 + "<impression-set key=\"" + key1 + "\" "
                 + "dismisses=\"" + dismiss1 + "\" views=\"" + views1
                 + "\" streak=\"" + streak1 + "\"/>\n"
@@ -377,7 +378,6 @@
         mAssistant.insertImpressions(key2, ci2);
         mAssistant.insertImpressions(key3, ci3);
 
-
         XmlSerializer serializer = new FastXmlSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 7983896..c23f226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -17,21 +17,20 @@
 package com.android.settingslib.core.instrumentation;
 
 import android.app.Activity;
-import android.content.Context;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
 import android.content.Intent;
 
 import android.os.SystemClock;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
 
 import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
 
 /**
  * Logs visibility change of a fragment.
  */
-public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause {
+public class VisibilityLoggerMixin implements LifecycleObserver {
 
     private static final String TAG = "VisibilityLoggerMixin";
 
@@ -55,7 +54,7 @@
         mMetricsFeature = metricsFeature;
     }
 
-    @Override
+    @OnLifecycleEvent(Event.ON_RESUME)
     public void onResume() {
         mVisibleTimestamp = SystemClock.elapsedRealtime();
         if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
@@ -63,7 +62,7 @@
         }
     }
 
-    @Override
+    @OnLifecycleEvent(Event.ON_PAUSE)
     public void onPause() {
         mVisibleTimestamp = 0;
         if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index a264886..1ab6afe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -18,6 +18,7 @@
 import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
 
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -30,6 +31,8 @@
 import android.content.Context;
 import android.content.Intent;
 
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.TestConfig;
@@ -39,6 +42,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
 import org.robolectric.annotation.Config;
 
 
@@ -110,6 +115,30 @@
                 .hidden(nullable(Context.class), anyInt());
     }
 
+    @Test
+    public void activityShouldBecomeVisibleAndHide() {
+        ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
+        TestActivity testActivity = ac.get();
+        MockitoAnnotations.initMocks(testActivity);
+        ac.create().start().resume();
+        verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt());
+        ac.pause().stop().destroy();
+        verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt());
+    }
+
+    public static class TestActivity extends FragmentActivity {
+        @Mock
+        MetricsFeatureProvider mMetricsFeatureProvider;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            VisibilityLoggerMixin mixin = new VisibilityLoggerMixin(
+                    TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider);
+            getLifecycle().addObserver(mixin);
+            super.onCreate(savedInstanceState);
+        }
+    }
+
     private final class TestInstrumentable implements Instrumentable {
 
         public static final int TEST_METRIC = 12345;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 7428149..1dc8e46 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -87,7 +87,6 @@
 
     private static final HashSet<String> mValidTables = new HashSet<String>();
 
-    private static final String DATABASE_JOURNAL_SUFFIX = "-journal";
     private static final String DATABASE_BACKUP_SUFFIX = "-backup";
 
     private static final String TABLE_SYSTEM = "system";
@@ -148,12 +147,7 @@
         }
         File databaseFile = mContext.getDatabasePath(getDatabaseName());
         if (databaseFile.exists()) {
-            databaseFile.delete();
-        }
-        File databaseJournalFile = mContext.getDatabasePath(getDatabaseName()
-                + DATABASE_JOURNAL_SUFFIX);
-        if (databaseJournalFile.exists()) {
-            databaseJournalFile.delete();
+            SQLiteDatabase.deleteDatabase(databaseFile);
         }
     }
 
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 3a41681..8a48e7b 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -124,6 +124,10 @@
     <string name="keyboardview_keycode_delete">Delete</string>
     <!-- Description of the button used to disable current carrier when the device supported embedded SIM. [CHAR LIMIT=30] -->
     <string name="disable_carrier_button_text">Disable eSIM</string>
+    <!-- Title of Error message when disabling current carrier failed for the device supported embedded SIM. [CHAR LIMIT=80] -->
+    <string name="error_disable_esim_title">Can\u2019t disable eSIM</string>
+    <!-- Description of Error message when disabling current carrier failed for the device supported embedded SIM. [CHAR LIMIT=80] -->
+    <string name="error_disable_esim_msg">The eSIM can\u2019t be disabled due to an error.</string>
     <!-- Description of the Enter button in a KeyboardView. [CHAR LIMIT=NONE] -->
     <string name="keyboardview_keycode_enter">Enter</string>
 
@@ -146,8 +150,8 @@
     <string name="kg_sim_pin_instructions">Enter SIM PIN.</string>
     <!-- Instructions for using the SIM PIN unlock screen when there's more than one SIM -->
     <string name="kg_sim_pin_instructions_multi">Enter SIM PIN for \"<xliff:g id="carrier" example="CARD 1">%1$s</xliff:g>\".</string>
-    <!-- Instructions for disabling eSIM carrier to unlock the phone with embedded SIM -->
-    <string name="kg_sim_lock_instructions_esim">Disable eSIM to use device without mobile service.</string>
+    <!-- Instructions for disabling eSIM carrier to unlock the phone with embedded SIM. This message follows the original SIM PIN/PUK message of device without embedded SIM. -->
+    <string name="kg_sim_lock_esim_instructions"><xliff:g id="previous_msg" example="Enter SIM PIN.">%1$s</xliff:g> Disable eSIM to use device without mobile service.</string>
     <!-- Instructions for using the PIN unlock screen -->
     <string name="kg_pin_instructions">Enter PIN</string>
     <!-- Instructions for using the password unlock screen -->
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
new file mode 100644
index 0000000..509cd1f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid
+        android:color="#e5e5e5" />
+    <corners android:radius="2dp" />
+</shape>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 9f6a946..100c2aa 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -25,87 +25,121 @@
     android:baselineAligned="false"
     android:clickable="false"
     android:clipChildren="false"
-    android:clipToPadding="false"
-    android:paddingTop="0dp"
-    android:gravity="center_vertical"
-    android:orientation="horizontal">
+    android:clipToPadding="false">
+
+    <View
+        android:id="@+id/qs_footer_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_gravity="top"
+        android:background="?android:attr/dividerHorizontal"/>
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="16dp"
+        android:layout_height="match_parent"
+        android:layout_marginTop="1dp"
+        android:layout_marginStart="8dp"
         android:layout_marginEnd="8dp"
-        android:gravity="end">
+        android:layout_gravity="center_vertical"
+        android:gravity="end" >
 
-        <com.android.keyguard.CarrierText
-            android:id="@+id/qs_carrier_text"
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1" >
+            <!-- Add an extra 8dp margin before carrier text without shifting it right -->
+            <android.widget.Space
+                android:layout_width="8dp"
+                android:layout_height="match_parent" />
+
+            <com.android.keyguard.CarrierText
+                android:id="@+id/qs_carrier_text"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center_vertical|start"
+                android:ellipsize="marquee"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textDirection="locale"
+                android:singleLine="true" />
+        </LinearLayout>
+
+        <FrameLayout
+            android:layout_width="24dp"
+            android:layout_height="match_parent" >
+            <View
+                android:id="@+id/qs_drag_handle_view"
+                android:layout_width="match_parent"
+                android:layout_height="4dp"
+                android:layout_marginTop="28dp"
+                android:background="@drawable/qs_footer_drag_handle" />
+        </FrameLayout>
+
+        <LinearLayout
+            android:id="@+id/qs_footer_actions_container"
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:gravity="center_vertical|start"
-            android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary"
-            android:textDirection="locale"
-            android:singleLine="true" />
-
-        <com.android.systemui.statusbar.phone.MultiUserSwitch
-            android:id="@+id/multi_user_switch"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:layout_alignParentEnd="true"
-            android:background="@drawable/ripple_drawable"
-            android:focusable="true">
-
-            <ImageView
-                android:id="@+id/multi_user_avatar"
-                android:layout_width="@dimen/multi_user_avatar_expanded_size"
-                android:layout_height="@dimen/multi_user_avatar_expanded_size"
-                android:layout_gravity="center"
-                android:scaleType="centerInside"/>
-        </com.android.systemui.statusbar.phone.MultiUserSwitch>
-
-        <com.android.systemui.statusbar.AlphaOptimizedImageView
-            android:id="@android:id/edit"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:background="?android:attr/selectableItemBackgroundBorderless"
-            android:clickable="true"
-            android:clipToPadding="false"
-            android:contentDescription="@string/accessibility_quick_settings_edit"
-            android:focusable="true"
-            android:padding="16dp"
-            android:src="@drawable/ic_mode_edit"
-            android:tint="?android:attr/colorForeground"/>
-
-        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-            android:id="@+id/settings_button_container"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:clipChildren="false"
-            android:clipToPadding="false">
-
-            <com.android.systemui.statusbar.phone.SettingsButton
-                android:id="@+id/settings_button"
-                style="@android:style/Widget.Material.Button.Borderless"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
+            android:gravity="end" >
+            <com.android.systemui.statusbar.phone.MultiUserSwitch
+                android:id="@+id/multi_user_switch"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_alignParentEnd="true"
                 android:background="@drawable/ripple_drawable"
-                android:contentDescription="@string/accessibility_quick_settings_settings"
-                android:src="@drawable/ic_settings_16dp"
-                android:tint="?android:attr/colorForeground"/>
+                android:focusable="true">
+
+                <ImageView
+                    android:id="@+id/multi_user_avatar"
+                    android:layout_width="@dimen/multi_user_avatar_expanded_size"
+                    android:layout_height="@dimen/multi_user_avatar_expanded_size"
+                    android:layout_gravity="center"
+                    android:scaleType="centerInside"/>
+            </com.android.systemui.statusbar.phone.MultiUserSwitch>
 
             <com.android.systemui.statusbar.AlphaOptimizedImageView
-                android:id="@+id/tuner_icon"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:paddingStart="36dp"
-                android:paddingEnd="4dp"
-                android:src="@drawable/tuner"
-                android:tint="?android:attr/textColorTertiary"
-                android:visibility="invisible"/>
+                android:id="@android:id/edit"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:background="?android:attr/selectableItemBackgroundBorderless"
+                android:clickable="true"
+                android:clipToPadding="false"
+                android:contentDescription="@string/accessibility_quick_settings_edit"
+                android:focusable="true"
+                android:padding="16dp"
+                android:src="@drawable/ic_mode_edit"
+                android:tint="?android:attr/colorForeground"/>
 
-        </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+                android:id="@+id/settings_button_container"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+
+                <com.android.systemui.statusbar.phone.SettingsButton
+                    android:id="@+id/settings_button"
+                    style="@android:style/Widget.Material.Button.Borderless"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:background="@drawable/ripple_drawable"
+                    android:contentDescription="@string/accessibility_quick_settings_settings"
+                    android:src="@drawable/ic_settings_16dp"
+                    android:tint="?android:attr/colorForeground"/>
+
+                <com.android.systemui.statusbar.AlphaOptimizedImageView
+                    android:id="@+id/tuner_icon"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:paddingStart="36dp"
+                    android:paddingEnd="4dp"
+                    android:src="@drawable/tuner"
+                    android:tint="?android:attr/textColorTertiary"
+                    android:visibility="invisible"/>
+
+            </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+        </LinearLayout>
     </LinearLayout>
 
 </com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index d1ef5d6..734c877 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -22,7 +22,7 @@
         android:id="@+id/left"
         android:layout_width="12dp"
         android:layout_height="12dp"
-        android:layout_gravity="left"
+        android:layout_gravity="left|top"
         android:tint="#ff000000"
         android:src="@drawable/rounded" />
     <ImageView
@@ -30,6 +30,6 @@
         android:layout_width="12dp"
         android:layout_height="12dp"
         android:tint="#ff000000"
-        android:layout_gravity="right"
+        android:layout_gravity="right|bottom"
         android:src="@drawable/rounded" />
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index c2b1009..a3118b0 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -84,7 +84,25 @@
         android:layout_height="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
         android:paddingStart="@dimen/screen_pinning_request_frame_padding"
-        android:paddingEnd="@dimen/screen_pinning_request_frame_padding" >
+        android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
+        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg_light"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="matrix"
+            android:src="@drawable/screen_pinning_light_bg_circ" />
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingEnd="@dimen/screen_pinning_request_inner_padding"
+            android:paddingStart="@dimen/screen_pinning_request_inner_padding"
+            android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+            android:scaleType="matrix"
+            android:src="@drawable/screen_pinning_bg_circ" />
 
         <ImageView
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index b5ef1d7..61fe906 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -78,7 +78,25 @@
         android:id="@+id/screen_pinning_home_group"
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
-        android:layout_weight="0" >
+        android:layout_weight="0"
+        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg_light"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:scaleType="matrix"
+            android:src="@drawable/screen_pinning_light_bg_circ" />
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:scaleType="matrix"
+            android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
+            android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+            android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+            android:src="@drawable/screen_pinning_bg_circ" />
 
         <ImageView
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index f3a6d44..d1ca2ce 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -80,7 +80,27 @@
         android:id="@+id/screen_pinning_home_group"
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
-        android:layout_weight="0" >
+        android:layout_weight="0"
+        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg_light"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:scaleType="matrix"
+            android:layout_marginLeft="@dimen/screen_pinning_request_seascape_padding_negative"
+            android:src="@drawable/screen_pinning_light_bg_circ" />
+
+        <ImageView
+            android:id="@+id/screen_pinning_home_bg"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:scaleType="matrix"
+            android:layout_marginLeft="@dimen/screen_pinning_request_seascape_button_offset"
+            android:paddingRight="@dimen/screen_pinning_request_inner_padding"
+            android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+            android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+            android:src="@drawable/screen_pinning_bg_circ" />
 
         <ImageView
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 117cd14..53dff05 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -59,6 +59,7 @@
             android:gravity="center"
             android:layout_gravity="end"
             android:translationZ="8dp"
+            android:clickable="true"
             android:orientation="vertical" >
 
             <TextView
@@ -76,7 +77,7 @@
                 android:id="@+id/ringer_icon"
                 style="@style/VolumeButtons"
                 android:background="?android:selectableItemBackgroundBorderless"
-                android:layout_width="@dimen/volume_button_size"
+                android:layout_width="@dimen/volume_dialog_panel_width"
                 android:layout_height="@dimen/volume_button_size"
                 android:tint="?android:attr/colorAccent"
                 android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 1d1f95b..cdd417f 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -61,7 +61,9 @@
                 android:layout_width="24dp"
                 android:layout_height="24dp"
                 android:background="?android:selectableItemBackgroundBorderless"
+                android:contentDescription="@string/accessibility_output_chooser"
                 style="@style/VolumeButtons"
+                android:clickable="false"
                 android:layout_centerVertical="true"
                 android:src="@drawable/ic_swap"
                 android:soundEffectsEnabled="false" />
@@ -69,19 +71,20 @@
     </LinearLayout>
     <FrameLayout
         android:id="@+id/volume_row_slider_frame"
-        android:padding="10dp"
+        android:padding="0dp"
         android:layout_width="@dimen/volume_dialog_panel_width"
+        android:layoutDirection="rtl"
         android:layout_height="150dp">
         <SeekBar
             android:id="@+id/volume_row_slider"
+            android:clickable="true"
             android:padding="0dp"
             android:layout_margin="0dp"
             android:layout_width="150dp"
             android:layout_height="@dimen/volume_dialog_panel_width"
+            android:layoutDirection="rtl"
             android:layout_gravity="center"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            android:rotation="270" />
+            android:rotation="90" />
     </FrameLayout>
 
     <com.android.keyguard.AlphaOptimizedImageButton
@@ -90,6 +93,7 @@
         android:padding="10dp"
         android:layout_width="@dimen/volume_button_size"
         android:layout_height="@dimen/volume_button_size"
+        android:background="?android:selectableItemBackgroundBorderless"
         android:soundEffectsEnabled="false" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6768470..ae910fe 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -124,7 +124,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,alarm
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -348,8 +348,7 @@
         <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
         <item>com.android.systemui.LatencyTester</item>
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
-        <item>com.android.systemui.RoundedCorners</item>
-        <item>com.android.systemui.EmulatedDisplayCutout</item>
+        <item>com.android.systemui.ScreenDecorations</item>
         <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
     </string-array>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index edda613..f9aa821 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -96,5 +96,7 @@
 
     <!-- For StatusBarIconContainer to tag its icon views -->
     <item type="id" name="status_bar_view_state_tag" />
+
+    <item type="id" name="display_cutout" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9b6af43..fadcbcd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -739,6 +739,8 @@
     <string name="quick_settings_wifi_on_label">Wi-Fi On</string>
     <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string>
+    <!-- QuickSettings: Alarm title [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_alarm_title">Alarm</string>
     <!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
     <string name="quick_settings_cast_title">Cast</string>
     <!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
@@ -1272,16 +1274,30 @@
     <!-- Content description for accessibility (not shown on the screen): volume dialog collapse button. [CHAR LIMIT=NONE] -->
     <string name="accessibility_volume_collapse">Collapse</string>
 
+    <!-- content description for audio output chooser [CHAR LIMIT=NONE]-->
+    <string name="accessibility_output_chooser">Switch output device</string>
+
     <!-- Screen pinning dialog title. -->
     <string name="screen_pinning_title">Screen is pinned</string>
     <!-- Screen pinning dialog description. -->
     <string name="screen_pinning_description">This keeps it in view until you unpin. Touch &amp; hold Back and Overview to unpin.</string>
+    <string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch &amp; hold Back and Home to unpin.</string>
     <!-- Screen pinning dialog description. -->
     <string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch &amp; hold Overview to unpin.</string>
+    <string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch &amp; hold Home to unpin.</string>
+    <!-- Notify use that they are in Lock-to-app -->
+    <string name="screen_pinning_toast">To unpin this screen, touch &amp; hold Back and Overview
+        buttons</string>
+    <string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch &amp; hold Back
+        and Home buttons</string>
     <!-- Screen pinning positive response. -->
     <string name="screen_pinning_positive">Got it</string>
     <!-- Screen pinning negative response. -->
     <string name="screen_pinning_negative">No thanks</string>
+    <!-- Enter/Exiting screen pinning indication. -->
+    <string name="screen_pinning_start">Screen pinned</string>
+    <string name="screen_pinning_exit">Screen unpinned</string>
+
 
     <!-- Hide quick settings tile confirmation title -->
     <string name="quick_settings_reset_confirmation_title">Hide <xliff:g id="tile_label" example="Hotspot">%1$s</xliff:g>?</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index cb5afec..b8a07cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -16,14 +16,18 @@
 
 package com.android.keyguard;
 
+import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.Button;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionInfo;
@@ -50,8 +54,17 @@
                 if (ACTION_DISABLE_ESIM.equals(intent.getAction())) {
                     int resultCode = getResultCode();
                     if (resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
-                        // TODO (b/62680294): Surface more info. to the end users for this failure.
                         Log.e(TAG, "Error disabling esim, result code = " + resultCode);
+                        AlertDialog.Builder builder =
+                                new AlertDialog.Builder(mContext)
+                                        .setMessage(R.string.error_disable_esim_msg)
+                                        .setTitle(R.string.error_disable_esim_title)
+                                        .setCancelable(false /* cancelable */)
+                                        .setNeutralButton(R.string.ok, null /* listener */);
+                        AlertDialog alertDialog = builder.create();
+                        alertDialog.getWindow().setType(
+                                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+                        alertDialog.show();
                     }
                 }
             }
@@ -101,14 +114,13 @@
 
     @Override
     public void onClick(View v) {
-        Intent intent = new Intent(mContext, KeyguardEsimArea.class);
-        intent.setAction(ACTION_DISABLE_ESIM);
+        Intent intent = new Intent(ACTION_DISABLE_ESIM);
         intent.setPackage(mContext.getPackageName());
-        PendingIntent callbackIntent = PendingIntent.getBroadcast(
+        PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
             mContext,
             0 /* requestCode */,
             intent,
-            PendingIntent.FLAG_UPDATE_CURRENT);
+            PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
         mEuiccManager
                 .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index e7432ba..703b205 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -131,7 +131,7 @@
         }
 
         if (isEsimLocked) {
-            msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
+            msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
         }
 
         mSecurityMessageDisplay.setMessage(msg);
@@ -187,6 +187,10 @@
             msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
             displayMessage = getContext().getString(msgId);
         }
+        if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+            displayMessage = getResources()
+                    .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+        }
         if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
         return displayMessage;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index afee8ec..347c979 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -181,7 +181,7 @@
             }
         }
         if (isEsimLocked) {
-            msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
+            msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
         }
         mSecurityMessageDisplay.setMessage(msg);
         mSimImageView.setImageTintList(ColorStateList.valueOf(color));
@@ -231,6 +231,10 @@
                     R.string.kg_password_puk_failed;
             displayMessage = getContext().getString(msgId);
         }
+        if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+            displayMessage = getResources()
+                    .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+        }
         if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
         return displayMessage;
diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
deleted file mode 100644
index 5d2e4d0..0000000
--- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
+++ /dev/null
@@ -1,129 +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.
- */
-
-package com.android.systemui;
-
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-
-/**
- * Emulates a display cutout by drawing its shape in an overlay as supplied by
- * {@link DisplayCutout}.
- */
-public class EmulatedDisplayCutout extends SystemUI implements ConfigurationListener {
-    private View mOverlay;
-    private boolean mAttached;
-    private WindowManager mWindowManager;
-
-    @Override
-    public void start() {
-        Dependency.get(ConfigurationController.class).addCallback(this);
-
-        mWindowManager = mContext.getSystemService(WindowManager.class);
-        updateAttached();
-    }
-
-    @Override
-    public void onOverlayChanged() {
-        updateAttached();
-    }
-
-    private void updateAttached() {
-        boolean shouldAttach = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
-        setAttached(shouldAttach);
-    }
-
-    private void setAttached(boolean attached) {
-        if (attached && !mAttached) {
-            if (mOverlay == null) {
-                mOverlay = new CutoutView(mContext);
-                mOverlay.setLayoutParams(getLayoutParams());
-            }
-            mWindowManager.addView(mOverlay, mOverlay.getLayoutParams());
-            mAttached = true;
-        } else if (!attached && mAttached) {
-            mWindowManager.removeView(mOverlay);
-            mAttached = false;
-        }
-    }
-
-    private WindowManager.LayoutParams getLayoutParams() {
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SLIPPERY
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
-                PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
-                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        lp.setTitle("EmulatedDisplayCutout");
-        lp.gravity = Gravity.TOP;
-        return lp;
-    }
-
-    private static class CutoutView extends View {
-        private final Paint mPaint = new Paint();
-        private final Path mBounds = new Path();
-
-        CutoutView(Context context) {
-            super(context);
-        }
-
-        @Override
-        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-            mBounds.reset();
-            if (insets.getDisplayCutout() != null) {
-                insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds);
-            }
-            invalidate();
-            return insets.consumeDisplayCutout();
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            if (!mBounds.isEmpty()) {
-                mPaint.setColor(Color.BLACK);
-                mPaint.setStyle(Paint.Style.FILL);
-
-                canvas.drawPath(mBounds, mPaint);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
deleted file mode 100644
index c960fa1..0000000
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ /dev/null
@@ -1,221 +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.
- */
-
-package com.android.systemui;
-
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
-import static com.android.systemui.tuner.TunablePadding.FLAG_END;
-
-import android.app.Fragment;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.provider.Settings.Secure;
-import android.support.annotation.VisibleForTesting;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import com.android.systemui.R.id;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NavigationBarFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.tuner.TunablePadding;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-
-public class RoundedCorners extends SystemUI implements Tunable {
-    public static final String SIZE = "sysui_rounded_size";
-    public static final String PADDING = "sysui_rounded_content_padding";
-
-    private int mRoundedDefault;
-    private View mOverlay;
-    private View mBottomOverlay;
-    private float mDensity;
-    private TunablePadding mQsPadding;
-    private TunablePadding mStatusBarPadding;
-    private TunablePadding mNavBarPadding;
-
-    @Override
-    public void start() {
-        mRoundedDefault = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_radius);
-        if (mRoundedDefault != 0) {
-            setupRounding();
-        }
-        int padding = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_content_padding);
-        if (padding != 0) {
-            setupPadding(padding);
-        }
-    }
-
-    private void setupRounding() {
-        mOverlay = LayoutInflater.from(mContext)
-                .inflate(R.layout.rounded_corners, null);
-        mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-        mOverlay.setAlpha(0);
-        mOverlay.findViewById(R.id.right).setRotation(90);
-
-        mContext.getSystemService(WindowManager.class)
-                .addView(mOverlay, getWindowLayoutParams());
-        mBottomOverlay = LayoutInflater.from(mContext)
-                .inflate(R.layout.rounded_corners, null);
-        mBottomOverlay.setAlpha(0);
-        mBottomOverlay.findViewById(R.id.right).setRotation(180);
-        mBottomOverlay.findViewById(R.id.left).setRotation(270);
-        WindowManager.LayoutParams layoutParams = getWindowLayoutParams();
-        layoutParams.gravity = Gravity.BOTTOM;
-        mContext.getSystemService(WindowManager.class)
-                .addView(mBottomOverlay, layoutParams);
-
-        DisplayMetrics metrics = new DisplayMetrics();
-        mContext.getSystemService(WindowManager.class)
-                .getDefaultDisplay().getMetrics(metrics);
-        mDensity = metrics.density;
-
-        Dependency.get(TunerService.class).addTunable(this, SIZE);
-
-        // Watch color inversion and invert the overlay as needed.
-        SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
-                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
-            @Override
-            protected void handleValueChanged(int value, boolean observedChange) {
-                int tint = value != 0 ? Color.WHITE : Color.BLACK;
-                ColorStateList tintList = ColorStateList.valueOf(tint);
-                ((ImageView) mOverlay.findViewById(id.left)).setImageTintList(tintList);
-                ((ImageView) mOverlay.findViewById(id.right)).setImageTintList(tintList);
-                ((ImageView) mBottomOverlay.findViewById(id.left)).setImageTintList(tintList);
-                ((ImageView) mBottomOverlay.findViewById(id.right)).setImageTintList(tintList);
-            }
-        };
-        setting.setListening(true);
-        setting.onChange(false);
-
-        mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft,
-                    int oldTop, int oldRight, int oldBottom) {
-                mOverlay.removeOnLayoutChangeListener(this);
-                mOverlay.animate()
-                        .alpha(1)
-                        .setDuration(1000)
-                        .start();
-                mBottomOverlay.animate()
-                        .alpha(1)
-                        .setDuration(1000)
-                        .start();
-            }
-        });
-    }
-
-    private void setupPadding(int padding) {
-        // Add some padding to all the content near the edge of the screen.
-        StatusBar sb = getComponent(StatusBar.class);
-        View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
-        if (statusBar != null) {
-            TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING,
-                    padding, FLAG_END);
-
-            FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar);
-            fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
-                    new TunablePaddingTagListener(padding, R.id.status_bar));
-            fragmentHostManager.addTagListener(QS.TAG,
-                    new TunablePaddingTagListener(padding, R.id.header));
-        }
-    }
-
-    private WindowManager.LayoutParams getWindowLayoutParams() {
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SLIPPERY
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
-                PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
-                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-        lp.setTitle("RoundedOverlay");
-        lp.gravity = Gravity.TOP;
-        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        return lp;
-    }
-
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (mOverlay == null) return;
-        if (SIZE.equals(key)) {
-            int size = mRoundedDefault;
-            try {
-                size = (int) (Integer.parseInt(newValue) * mDensity);
-            } catch (Exception e) {
-            }
-            setSize(mOverlay.findViewById(R.id.left), size);
-            setSize(mOverlay.findViewById(R.id.right), size);
-            setSize(mBottomOverlay.findViewById(R.id.left), size);
-            setSize(mBottomOverlay.findViewById(R.id.right), size);
-        }
-    }
-
-    private void setSize(View view, int pixelSize) {
-        LayoutParams params = view.getLayoutParams();
-        params.width = pixelSize;
-        params.height = pixelSize;
-        view.setLayoutParams(params);
-    }
-
-    @VisibleForTesting
-    static class TunablePaddingTagListener implements FragmentListener {
-
-        private final int mPadding;
-        private final int mId;
-        private TunablePadding mTunablePadding;
-
-        public TunablePaddingTagListener(int padding, int id) {
-            mPadding = padding;
-            mId = id;
-        }
-
-        @Override
-        public void onFragmentViewCreated(String tag, Fragment fragment) {
-            if (mTunablePadding != null) {
-                mTunablePadding.destroy();
-            }
-            View view = fragment.getView();
-            if (mId != 0) {
-                view = view.findViewById(mId);
-            }
-            mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
-                    FLAG_START | FLAG_END);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
new file mode 100644
index 0000000..0b3e9e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static com.android.systemui.tuner.TunablePadding.FLAG_START;
+import static com.android.systemui.tuner.TunablePadding.FLAG_END;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings.Secure;
+import android.support.annotation.VisibleForTesting;
+import android.util.DisplayMetrics;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.tuner.TunablePadding;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+/**
+ * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
+ * for antialiasing and emulation purposes.
+ */
+public class ScreenDecorations extends SystemUI implements Tunable {
+    public static final String SIZE = "sysui_rounded_size";
+    public static final String PADDING = "sysui_rounded_content_padding";
+
+    private int mRoundedDefault;
+    private View mOverlay;
+    private View mBottomOverlay;
+    private float mDensity;
+    private WindowManager mWindowManager;
+    private boolean mLandscape;
+
+    @Override
+    public void start() {
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+        mRoundedDefault = mContext.getResources().getDimensionPixelSize(
+                R.dimen.rounded_corner_radius);
+        if (mRoundedDefault != 0 || shouldDrawCutout()) {
+            setupDecorations();
+        }
+        int padding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.rounded_corner_content_padding);
+        if (padding != 0) {
+            setupPadding(padding);
+        }
+    }
+
+    private void setupDecorations() {
+        mOverlay = LayoutInflater.from(mContext)
+                .inflate(R.layout.rounded_corners, null);
+        ((ViewGroup)mOverlay).addView(new DisplayCutoutView(mContext, true,
+                this::updateWindowVisibilities));
+        mBottomOverlay = LayoutInflater.from(mContext)
+                .inflate(R.layout.rounded_corners, null);
+        ((ViewGroup)mBottomOverlay).addView(new DisplayCutoutView(mContext, false,
+                this::updateWindowVisibilities));
+
+        mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        mOverlay.setAlpha(0);
+
+        mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        mBottomOverlay.setAlpha(0);
+
+        updateViews();
+
+        mWindowManager.addView(mOverlay, getWindowLayoutParams());
+        mWindowManager.addView(mBottomOverlay, getBottomLayoutParams());
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        mWindowManager.getDefaultDisplay().getMetrics(metrics);
+        mDensity = metrics.density;
+
+        Dependency.get(TunerService.class).addTunable(this, SIZE);
+
+        // Watch color inversion and invert the overlay as needed.
+        SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
+            @Override
+            protected void handleValueChanged(int value, boolean observedChange) {
+                int tint = value != 0 ? Color.WHITE : Color.BLACK;
+                ColorStateList tintList = ColorStateList.valueOf(tint);
+                ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+                ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+                ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+                ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+            }
+        };
+        setting.setListening(true);
+        setting.onChange(false);
+
+        mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                mOverlay.removeOnLayoutChangeListener(this);
+                mOverlay.animate()
+                        .alpha(1)
+                        .setDuration(1000)
+                        .start();
+                mBottomOverlay.animate()
+                        .alpha(1)
+                        .setDuration(1000)
+                        .start();
+            }
+        });
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        boolean newLanscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
+        if (newLanscape != mLandscape) {
+            mLandscape = newLanscape;
+
+            if (mOverlay != null) {
+                updateLayoutParams();
+                updateViews();
+            }
+        }
+        if (shouldDrawCutout() && mOverlay == null) {
+            setupDecorations();
+        }
+    }
+
+    private void updateViews() {
+        View topLeft = mOverlay.findViewById(R.id.left);
+        View topRight = mOverlay.findViewById(R.id.right);
+        View bottomLeft = mBottomOverlay.findViewById(R.id.left);
+        View bottomRight = mBottomOverlay.findViewById(R.id.right);
+        if (mLandscape) {
+            // Flip corners
+            View tmp = topRight;
+            topRight = bottomLeft;
+            bottomLeft = tmp;
+        }
+        updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
+        updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
+        updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+        updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+
+        updateWindowVisibilities();
+    }
+
+    private void updateView(View v, int gravity, int rotation) {
+        ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity;
+        v.setRotation(rotation);
+    }
+
+    private void updateWindowVisibilities() {
+        updateWindowVisibility(mOverlay);
+        updateWindowVisibility(mBottomOverlay);
+    }
+
+    private void updateWindowVisibility(View overlay) {
+        boolean visibleForCutout = shouldDrawCutout()
+                && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
+        boolean visibleForRoundedCorners = mRoundedDefault > 0;
+        overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
+                ? View.VISIBLE : View.GONE);
+    }
+
+    private boolean shouldDrawCutout() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
+    }
+
+    private void setupPadding(int padding) {
+        // Add some padding to all the content near the edge of the screen.
+        StatusBar sb = getComponent(StatusBar.class);
+        View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
+        if (statusBar != null) {
+            TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING,
+                    padding, FLAG_END);
+
+            FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar);
+            fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
+                    new TunablePaddingTagListener(padding, R.id.status_bar));
+            fragmentHostManager.addTagListener(QS.TAG,
+                    new TunablePaddingTagListener(padding, R.id.header));
+        }
+    }
+
+    @VisibleForTesting
+    WindowManager.LayoutParams getWindowLayoutParams() {
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+                PixelFormat.TRANSLUCENT);
+        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+        lp.setTitle("ScreenDecorOverlay");
+        lp.gravity = Gravity.TOP | Gravity.LEFT;
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        if (mLandscape) {
+            lp.width = WRAP_CONTENT;
+            lp.height = MATCH_PARENT;
+        }
+        return lp;
+    }
+
+    private WindowManager.LayoutParams getBottomLayoutParams() {
+        WindowManager.LayoutParams lp = getWindowLayoutParams();
+        lp.setTitle("ScreenDecorOverlayBottom");
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        return lp;
+    }
+
+    private void updateLayoutParams() {
+        mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
+        mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (mOverlay == null) return;
+        if (SIZE.equals(key)) {
+            int size = mRoundedDefault;
+            try {
+                size = (int) (Integer.parseInt(newValue) * mDensity);
+            } catch (Exception e) {
+            }
+            setSize(mOverlay.findViewById(R.id.left), size);
+            setSize(mOverlay.findViewById(R.id.right), size);
+            setSize(mBottomOverlay.findViewById(R.id.left), size);
+            setSize(mBottomOverlay.findViewById(R.id.right), size);
+        }
+    }
+
+    private void setSize(View view, int pixelSize) {
+        LayoutParams params = view.getLayoutParams();
+        params.width = pixelSize;
+        params.height = pixelSize;
+        view.setLayoutParams(params);
+    }
+
+    @VisibleForTesting
+    static class TunablePaddingTagListener implements FragmentListener {
+
+        private final int mPadding;
+        private final int mId;
+        private TunablePadding mTunablePadding;
+
+        public TunablePaddingTagListener(int padding, int id) {
+            mPadding = padding;
+            mId = id;
+        }
+
+        @Override
+        public void onFragmentViewCreated(String tag, Fragment fragment) {
+            if (mTunablePadding != null) {
+                mTunablePadding.destroy();
+            }
+            View view = fragment.getView();
+            if (mId != 0) {
+                view = view.findViewById(mId);
+            }
+            mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
+                    FLAG_START | FLAG_END);
+        }
+    }
+
+    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener {
+
+        private final DisplayInfo mInfo = new DisplayInfo();
+        private final Paint mPaint = new Paint();
+        private final Rect mBoundingRect = new Rect();
+        private final Path mBoundingPath = new Path();
+        private final int[] mLocation = new int[2];
+        private final boolean mStart;
+        private final Runnable mVisibilityChangedListener;
+
+        public DisplayCutoutView(Context context, boolean start,
+                Runnable visibilityChangedListener) {
+            super(context);
+            mStart = start;
+            mVisibilityChangedListener = visibilityChangedListener;
+            setId(R.id.display_cutout);
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
+                    getHandler());
+            update();
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            getLocationOnScreen(mLocation);
+            canvas.translate(-mLocation[0], -mLocation[1]);
+            if (!mBoundingPath.isEmpty()) {
+                mPaint.setColor(Color.BLACK);
+                mPaint.setStyle(Paint.Style.FILL);
+                canvas.drawPath(mBoundingPath, mPaint);
+            }
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId == getDisplay().getDisplayId()) {
+                update();
+            }
+        }
+
+        private void update() {
+            requestLayout();
+            getDisplay().getDisplayInfo(mInfo);
+            mBoundingRect.setEmpty();
+            mBoundingPath.reset();
+            int newVisible;
+            if (hasCutout()) {
+                mBoundingRect.set(mInfo.displayCutout.getBoundingRect());
+                mInfo.displayCutout.getBounds().getBoundaryPath(mBoundingPath);
+                newVisible = VISIBLE;
+            } else {
+                newVisible = GONE;
+            }
+            if (newVisible != getVisibility()) {
+                setVisibility(newVisible);
+                mVisibilityChangedListener.run();
+            }
+        }
+
+        private boolean hasCutout() {
+            if (mInfo.displayCutout == null) {
+                return false;
+            }
+            DisplayCutout displayCutout = mInfo.displayCutout.calculateRelativeTo(
+                    new Rect(0, 0, mInfo.logicalWidth, mInfo.logicalHeight));
+            if (mStart) {
+                return displayCutout.getSafeInsetLeft() > 0
+                        || displayCutout.getSafeInsetTop() > 0;
+            } else {
+                return displayCutout.getSafeInsetRight() > 0
+                        || displayCutout.getSafeInsetBottom() > 0;
+            }
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            if (mBoundingRect.isEmpty()) {
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                return;
+            }
+            setMeasuredDimension(
+                    resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
+                    resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 89bc82f..43b918d 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -15,13 +15,19 @@
  */
 package com.android.systemui;
 
+import android.annotation.StringRes;
 import android.content.Context;
 import android.view.WindowManager;
 import android.widget.Toast;
+import static android.widget.Toast.Duration;
 
 public class SysUIToast {
 
-    public static Toast makeText(Context context, CharSequence text, int duration) {
+    public static Toast makeText(Context context, @StringRes int resId, @Duration int duration) {
+        return makeText(context, context.getString(resId), duration);
+    }
+
+    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
         Toast toast = Toast.makeText(context, text, duration);
         toast.getWindowParams().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 238ab29..c28b7ee 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -147,6 +147,7 @@
     private boolean mHasTelephony;
     private boolean mHasVibrator;
     private boolean mHasLogoutButton;
+    private boolean mHasLockdownButton;
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
     private final ScreenshotHelper mScreenshotHelper;
@@ -311,6 +312,7 @@
 
         ArraySet<String> addedKeys = new ArraySet<String>();
         mHasLogoutButton = false;
+        mHasLockdownButton = false;
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
@@ -341,6 +343,7 @@
                             Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0
                         && shouldDisplayLockdown()) {
                     mItems.add(getLockdownAction());
+                    mHasLockdownButton = true;
                 }
             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
                 mItems.add(getVoiceAssistAction());
@@ -871,10 +874,9 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             Action action = getItem(position);
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
-            // When there is no logout button, only power off and restart should be in white
-            // background, thus setting division view at third item; with logout button being the
-            // third item, set the division view at fourth item instead.
-            if (position == (mHasLogoutButton ? 3 : 2)) {
+            // Power off, restart, logout (if present) and lockdown (if present) should be in white
+            // background. Set the division based on which buttons are currently being displayed.
+            if (position == 2 + (mHasLogoutButton ? 1 : 0) + (mHasLockdownButton ? 1 : 0)) {
                 HardwareUiLayout.get(parent).setDivisionView(view);
             }
             return view;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8501519..eedc50f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -709,8 +709,7 @@
                     mSecondaryDisplayShowing, true /* forceCallbacks */);
         } else {
             // The system's keyguard is disabled or missing.
-            setShowingLocked(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()),
-                    mSecondaryDisplayShowing, true);
+            setShowingLocked(false, mSecondaryDisplayShowing, true);
         }
 
         mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index b43e99b..e661fa7 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
@@ -242,7 +243,9 @@
                 }
 
                 // Show the correct version of low battery warning if needed
-                maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+                ThreadUtils.postOnBackgroundThread(() -> {
+                    maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+                });
 
             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 mScreenOffTime = SystemClock.elapsedRealtime();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index f960dc5..2a2bc09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -51,10 +51,11 @@
     public AutoAddTracker(Context context) {
         mContext = context;
         mAutoAdded = new ArraySet<>(getAdded());
+        // TODO: remove migration code and shared preferences keys after P release
         for (String[] convertPref : CONVERT_PREFS) {
             if (Prefs.getBoolean(context, convertPref[0], false)) {
                 setTileAdded(convertPref[1]);
-                Prefs.putBoolean(context, convertPref[0], false);
+                Prefs.remove(context, convertPref[0]);
             }
         }
         mContext.getContentResolver().registerContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0d592..6ccb817 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -36,7 +36,6 @@
 public class QSContainerImpl extends FrameLayout {
 
     private final Point mSizePoint = new Point();
-    private final Path mClipPath = new Path();
 
     private int mHeightOverride = -1;
     protected View mQSPanel;
@@ -46,7 +45,6 @@
     private QSCustomizer mQSCustomizer;
     private View mQSFooter;
     private View mBackground;
-    private float mRadius;
     private int mSideMargins;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@
         mQSCustomizer = findViewById(R.id.qs_customize);
         mQSFooter = findViewById(R.id.qs_footer);
         mBackground = findViewById(R.id.quick_settings_background);
-        mRadius = getResources().getDimensionPixelSize(
-                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
         mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         setClickable(true);
@@ -115,18 +111,6 @@
         updateExpansion();
     }
 
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        boolean ret;
-        canvas.save();
-        if (child != mQSCustomizer) {
-            canvas.clipPath(mClipPath);
-        }
-        ret = super.drawChild(canvas, child, drawingTime);
-        canvas.restore();
-        return ret;
-    }
-
     /**
      * Overrides the height of this view (post-layout), so that the content is clipped to that
      * height and the background is set to that height.
@@ -146,10 +130,6 @@
         mQSFooter.setTranslationY(height - mQSFooter.getHeight());
         mBackground.setTop(mQSPanel.getTop());
         mBackground.setBottom(height);
-
-        ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
-                mRadius,
-                mClipPath);
     }
 
     protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 92475da..9c87e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -75,6 +75,7 @@
     private boolean mListening;
 
     private boolean mShowEmergencyCallsOnly;
+    private View mDivider;
     protected MultiUserSwitch mMultiUserSwitch;
     private ImageView mMultiUserAvatar;
 
@@ -84,6 +85,8 @@
     protected View mEdit;
     private TouchAnimator mAnimator;
 
+    private View mActionsContainer;
+
     public QSFooterImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -91,8 +94,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        Resources res = getResources();
-
+        mDivider = findViewById(R.id.qs_footer_divider);
         mEdit = findViewById(android.R.id.edit);
         mEdit.setOnClickListener(view ->
                 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
@@ -107,6 +109,8 @@
         mMultiUserSwitch = findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
 
+        mActionsContainer = findViewById(R.id.qs_footer_actions_container);
+
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
         ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
@@ -158,10 +162,9 @@
     @Nullable
     private TouchAnimator createSettingsAlphaAnimator() {
         return new TouchAnimator.Builder()
-                .addFloat(mEdit, "alpha", 0, 1)
-                .addFloat(mMultiUserSwitch, "alpha", 0, 1)
+                .addFloat(mDivider, "alpha", 0, 1)
                 .addFloat(mCarrierText, "alpha", 0, 1)
-                .addFloat(mSettingsButton, "alpha", 0, 1)
+                .addFloat(mActionsContainer, "alpha", 0, 1)
                 .build();
     }
 
@@ -269,6 +272,11 @@
 
     @Override
     public void onClick(View v) {
+        // Don't do anything until view are unhidden
+        if (!mExpanded) {
+            return;
+        }
+
         if (v == mSettingsButton) {
             if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
                 // If user isn't setup just unlock the device and dump them back at SUW.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 669439d..d8e1051 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -69,7 +69,7 @@
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater =inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
         return inflater.inflate(R.layout.qs_panel, container, false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 77c3bfa..bf9746e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -23,6 +23,7 @@
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.AlarmTile;
 import com.android.systemui.qs.tiles.BatterySaverTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
 import com.android.systemui.qs.tiles.CastTile;
@@ -69,6 +70,7 @@
         else if (tileSpec.equals("saver")) return new DataSaverTile(mHost);
         else if (tileSpec.equals("night")) return new NightDisplayTile(mHost);
         else if (tileSpec.equals("nfc")) return new NfcTile(mHost);
+        else if (tileSpec.equals("alarm")) return new AlarmTile(mHost);
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec);
         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
new file mode 100644
index 0000000..ff3fe73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.service.quicksettings.Tile.STATE_ACTIVE;
+import static android.service.quicksettings.Tile.STATE_UNAVAILABLE;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.QS_ALARM;
+import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm;
+
+import android.app.AlarmManager.AlarmClockInfo;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.provider.AlarmClock;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+public class AlarmTile extends QSTileImpl implements NextAlarmChangeCallback {
+    private final NextAlarmController mController;
+    private String mNextAlarm;
+    private PendingIntent mIntent;
+
+    public AlarmTile(QSTileHost host) {
+        super(host);
+        mController = Dependency.get(NextAlarmController.class);
+    }
+
+    @Override
+    public State newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    protected void handleClick() {
+        if (mIntent != null) {
+            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(mIntent);
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object arg) {
+        state.state = mNextAlarm != null ? STATE_ACTIVE : STATE_UNAVAILABLE;
+        state.label = getTileLabel();
+        state.secondaryLabel = mNextAlarm;
+        state.icon = ResourceIcon.get(R.drawable.stat_sys_alarm);
+        ((BooleanState) state).value = mNextAlarm != null;
+    }
+
+    @Override
+    public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
+        if (nextAlarm != null) {
+            mNextAlarm = formatNextAlarm(mContext, nextAlarm);
+            mIntent = nextAlarm.getShowIntent();
+        } else {
+            mNextAlarm = null;
+            mIntent = null;
+        }
+        refreshState();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return QS_ALARM;
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return new Intent(AlarmClock.ACTION_SET_ALARM);
+    }
+
+    @Override
+    protected void handleSetListening(boolean listening) {
+        if (listening) {
+            mController.addCallback(this);
+        } else {
+            mController.removeCallback(this);
+        }
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.status_bar_alarm);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 316ad16..57f7818 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -23,15 +23,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -43,6 +40,9 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.ArrayList;
@@ -233,11 +233,30 @@
                         .setVisibility(View.INVISIBLE);
             }
 
+            StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+            NavigationBarView navigationBarView = statusBar.getNavigationBarView();
+            final boolean recentsVisible = navigationBarView != null
+                    && navigationBarView.isRecentsButtonVisible();
             boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
+            int descriptionStringResId;
+            if (recentsVisible) {
+                mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
+                mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
+                mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
+                descriptionStringResId = touchExplorationEnabled
+                        ? R.string.screen_pinning_description_accessible
+                        : R.string.screen_pinning_description;
+            } else {
+                mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(INVISIBLE);
+                mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(VISIBLE);
+                mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(VISIBLE);
+                descriptionStringResId = touchExplorationEnabled
+                        ? R.string.screen_pinning_description_recents_invisible_accessible
+                        : R.string.screen_pinning_description_recents_invisible;
+            }
+
             ((TextView) mLayout.findViewById(R.id.screen_pinning_description))
-                    .setText(touchExplorationEnabled
-                            ? R.string.screen_pinning_description_accessible
-                            : R.string.screen_pinning_description);
+                    .setText(descriptionStringResId);
             final int backBgVisibility = touchExplorationEnabled ? View.INVISIBLE : View.VISIBLE;
             mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
             mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 0132fa8..bf4a225 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,6 +293,7 @@
             sharingIntent.setType("image/png");
             sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+            sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
             // Create a share action for the notification. Note, we proxy the call to
             // ScreenshotActionReceiver because RemoteViews currently forces an activity options
@@ -310,7 +311,9 @@
 
             Intent editIntent = new Intent(Intent.ACTION_EDIT);
             editIntent.setType("image/png");
-            editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+            editIntent.setData(uri);
+            editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
             // Create a edit action for the notification the same way.
             PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
@@ -902,6 +905,7 @@
             Intent chooserIntent = Intent.createChooser(sharingIntent, null,
                     chooseAction.getIntentSender())
                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+
             ActivityOptions opts = ActivityOptions.makeBasic();
             opts.setDisallowEnterPictureInPictureWhileLaunching(true);
             context.startActivityAsUser(chooserIntent, opts.toBundle(), UserHandle.CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 79e9f7b..11bdf6b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.support.annotation.VisibleForTesting;
 import android.util.Pair;
 
@@ -90,6 +91,8 @@
     private static final int MSG_FINGERPRINT_ERROR             = 42 << MSG_SHIFT;
     private static final int MSG_FINGERPRINT_HIDE              = 43 << MSG_SHIFT;
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
+    private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
+    private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -148,6 +151,8 @@
         default void clickTile(ComponentName tile) { }
 
         default void handleSystemKey(int arg1) { }
+        default void showPinningEnterExitToast(boolean entering) { }
+        default void showPinningEscapeToast() { }
         default void handleShowGlobalActionsMenu() { }
         default void handleShowShutdownUi(boolean isReboot, String reason) { }
 
@@ -453,6 +458,21 @@
     }
 
     @Override
+    public void showPinningEnterExitToast(boolean entering) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ENTER_EXIT, entering).sendToTarget();
+        }
+    }
+
+    @Override
+    public void showPinningEscapeToast() {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ESCAPE).sendToTarget();
+        }
+    }
+
+
+    @Override
     public void showGlobalActionsMenu() {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS);
@@ -767,6 +787,16 @@
                         mCallbacks.get(i).showChargingAnimation(msg.arg1);
                     }
                     break;
+                case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).showPinningEnterExitToast((Boolean) msg.obj);
+                    }
+                    break;
+                case MSG_SHOW_PINNING_TOAST_ESCAPE:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).showPinningEscapeToast();
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 8325df7..cad956c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -802,10 +802,6 @@
         updateRelativeOffset();
     }
 
-    public void setDarkOffsetX(int offsetX) {
-        mShelfIcons.setDarkOffsetX(offsetX);
-    }
-
     private class ShelfState extends ExpandableViewState {
         private float openedAmount;
         private boolean hasItemsInStableShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
index 3491f81..c214171 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.support.v4.view.AsyncLayoutInflater;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -30,15 +31,23 @@
  * An inflater task that asynchronously inflates a ExpandableNotificationRow
  */
 public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
+
+    private static final String TAG = "RowInflaterTask";
+    private static final boolean TRACE_ORIGIN = true;
+
     private RowInflationFinishedListener mListener;
     private NotificationData.Entry mEntry;
     private boolean mCancelled;
+    private Throwable mInflateOrigin;
 
     /**
      * Inflates a new notificationView. This should not be called twice on this object
      */
     public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
             RowInflationFinishedListener listener) {
+        if (TRACE_ORIGIN) {
+            mInflateOrigin = new Throwable("inflate requested here");
+        }
         mListener = listener;
         AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
         mEntry = entry;
@@ -54,8 +63,16 @@
     @Override
     public void onInflateFinished(View view, int resid, ViewGroup parent) {
         if (!mCancelled) {
-            mEntry.onInflationTaskFinished();
-            mListener.onInflationFinished((ExpandableNotificationRow) view);
+            try {
+                mEntry.onInflationTaskFinished();
+                mListener.onInflationFinished((ExpandableNotificationRow) view);
+            } catch (Throwable t) {
+                if (mInflateOrigin != null) {
+                    Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
+                    t.addSuppressed(mInflateOrigin);
+                }
+                throw t;
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 36f9f6b..b220686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -14,16 +14,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.AlarmManager.AlarmClockInfo;
 import android.content.Context;
 import android.os.Handler;
-import android.os.Looper;
 import android.provider.Settings.Secure;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
 import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.SecureSetting;
@@ -31,27 +28,37 @@
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
 /**
  * Manages which tiles should be automatically added to QS.
  */
 public class AutoTileManager {
-
     public static final String HOTSPOT = "hotspot";
     public static final String SAVER = "saver";
     public static final String INVERSION = "inversion";
     public static final String WORK = "work";
     public static final String NIGHT = "night";
+    public static final String ALARM = "alarm";
+
     private final Context mContext;
     private final QSTileHost mHost;
     private final Handler mHandler;
     private final AutoAddTracker mAutoTracker;
 
     public AutoTileManager(Context context, QSTileHost host) {
-        mAutoTracker = new AutoAddTracker(context);
+        this(context, new AutoAddTracker(context), host,
+            new Handler(Dependency.get(Dependency.BG_LOOPER)));
+    }
+
+    @VisibleForTesting
+    AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+            Handler handler) {
+        mAutoTracker = autoAddTracker;
         mContext = context;
         mHost = host;
-        mHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
+        mHandler = handler;
         if (!mAutoTracker.isAdded(HOTSPOT)) {
             Dependency.get(HotspotController.class).addCallback(mHotspotCallback);
         }
@@ -76,20 +83,25 @@
         if (!mAutoTracker.isAdded(WORK)) {
             Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback);
         }
-
         if (!mAutoTracker.isAdded(NIGHT)
-                && ColorDisplayController.isAvailable(mContext)) {
+            && ColorDisplayController.isAvailable(mContext)) {
             Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
         }
+        if (!mAutoTracker.isAdded(ALARM)) {
+            Dependency.get(NextAlarmController.class).addCallback(mNextAlarmChangeCallback);
+        }
     }
 
     public void destroy() {
-        mColorsSetting.setListening(false);
+        if (mColorsSetting != null) {
+            mColorsSetting.setListening(false);
+        }
         mAutoTracker.destroy();
         Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
         Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
         Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
         Dependency.get(ColorDisplayController.class).setListener(null);
+        Dependency.get(NextAlarmController.class).removeCallback(mNextAlarmChangeCallback);
     }
 
     private final ManagedProfileController.Callback mProfileCallback =
@@ -138,6 +150,19 @@
         }
     };
 
+    private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
+        @Override
+        public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
+            if (mAutoTracker.isAdded(ALARM)) return;
+            if (nextAlarm != null) {
+                mHost.addTile(ALARM);
+                mAutoTracker.setTileAdded(ALARM);
+                mHandler.post(() -> Dependency.get(NextAlarmController.class)
+                    .removeCallback(mNextAlarmChangeCallback));
+            }
+        }
+    };
+
     @VisibleForTesting
     final ColorDisplayController.Callback mColorDisplayCallback =
             new ColorDisplayController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 65c45a3..1239a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -22,11 +22,13 @@
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
+import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.annotation.IdRes;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -69,6 +71,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import android.widget.Button;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -153,6 +156,18 @@
     private Animator mRotateShowAnimator;
     private Animator mRotateHideAnimator;
 
+    private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+        @Override
+        public void onConnectionChanged(boolean isConnected) {
+            mNavigationBarView.onOverviewProxyConnectionChanged(isConnected);
+            updateScreenPinningGestures();
+        }
+
+        @Override
+        public void onRecentsAnimationStarted() {
+            mNavigationBarView.setRecentsAnimationStarted(true);
+        }
+    };
 
     // ----- Fragment Lifecycle Callbacks -----
 
@@ -239,12 +254,14 @@
         filter.addAction(Intent.ACTION_SCREEN_ON);
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
         notifyNavigationBarScreenOn();
+        mOverviewProxyService.addCallback(mOverviewProxyListener);
     }
 
     @Override
     public void onDestroyView() {
         super.onDestroyView();
         mNavigationBarView.getLightTransitionsController().destroy(getContext());
+        mOverviewProxyService.removeCallback(mOverviewProxyListener);
         getContext().unregisterReceiver(mBroadcastReceiver);
     }
 
@@ -514,6 +531,7 @@
         if (masked != mDisabledFlags1) {
             mDisabledFlags1 = masked;
             if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
+            updateScreenPinningGestures();
         }
     }
 
@@ -528,7 +546,7 @@
     private boolean shouldDisableNavbarGestures() {
         return !mStatusBar.isDeviceProvisioned()
                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
-                || mOverviewProxyService.getProxy() != null;
+                || mNavigationBarView.getRecentsButton().getVisibility() != View.VISIBLE;
     }
 
     private void repositionNavigationBar() {
@@ -540,6 +558,24 @@
                 ((View) mNavigationBarView.getParent()).getLayoutParams());
     }
 
+    private void updateScreenPinningGestures() {
+        if (mNavigationBarView == null) {
+            return;
+        }
+
+        // Change the cancel pin gesture to home and back if recents button is invisible
+        boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
+        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+        ButtonDispatcher backButton = mNavigationBarView.getBackButton();
+        if (recentsVisible) {
+            homeButton.setOnLongClickListener(this::onHomeLongClick);
+            backButton.setOnLongClickListener(this::onLongPressBackRecents);
+        } else {
+            homeButton.setOnLongClickListener(this::onLongPressBackHome);
+            backButton.setOnLongClickListener(this::onLongPressBackHome);
+        }
+    }
+
     private void notifyNavigationBarScreenOn() {
         mNavigationBarView.notifyScreenOn();
     }
@@ -555,11 +591,9 @@
 
         ButtonDispatcher backButton = mNavigationBarView.getBackButton();
         backButton.setLongClickable(true);
-        backButton.setOnLongClickListener(this::onLongPressBackRecents);
 
         ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
         homeButton.setOnTouchListener(this::onHomeTouch);
-        homeButton.setOnLongClickListener(this::onHomeLongClick);
 
         ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -569,6 +603,7 @@
         ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
         rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
         rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
+        updateScreenPinningGestures();
     }
 
     private boolean onHomeTouch(View v, MotionEvent event) {
@@ -649,20 +684,29 @@
         mCommandQueue.toggleRecentApps();
     }
 
+    private boolean onLongPressBackHome(View v) {
+        return onLongPressNavigationButtons(v, R.id.back, R.id.home);
+    }
+
+    private boolean onLongPressBackRecents(View v) {
+        return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
+    }
+
     /**
-     * This handles long-press of both back and recents.  They are
-     * handled together to capture them both being long-pressed
+     * This handles long-press of both back and recents/home. Back is the common button with
+     * combination of recents if it is visible or home if recents is invisible.
+     * They are handled together to capture them both being long-pressed
      * at the same time to exit screen pinning (lock task).
      *
-     * When accessibility mode is on, only a long-press from recents
+     * When accessibility mode is on, only a long-press from recents/home
      * is required to exit.
      *
      * In all other circumstances we try to pass through long-press events
      * for Back, so that apps can still use it.  Which can be from two things.
      * 1) Not currently in screen pinning (lock task).
-     * 2) Back is long-pressed without recents.
+     * 2) Back is long-pressed without recents/home.
      */
-    private boolean onLongPressBackRecents(View v) {
+    private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
         try {
             boolean sendBackLongPress = false;
             IActivityManager activityManager = ActivityManagerNative.getDefault();
@@ -670,6 +714,7 @@
             boolean inLockTaskMode = activityManager.isInLockTaskMode();
             if (inLockTaskMode && !touchExplorationEnabled) {
                 long time = System.currentTimeMillis();
+
                 // If we recently long-pressed the other button then they were
                 // long-pressed 'together'
                 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
@@ -677,26 +722,32 @@
                     // When exiting refresh disabled flags.
                     mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
                     return true;
-                } else if ((v.getId() == R.id.back)
-                        && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
-                    // If we aren't pressing recents right now then they presses
-                    // won't be together, so send the standard long-press action.
-                    sendBackLongPress = true;
+                } else if (v.getId() == btnId1) {
+                    ButtonDispatcher button = btnId2 == R.id.recent_apps
+                            ? mNavigationBarView.getRecentsButton()
+                            : mNavigationBarView.getHomeButton();
+                    if (!button.getCurrentView().isPressed()) {
+                        // If we aren't pressing recents/home right now then they presses
+                        // won't be together, so send the standard long-press action.
+                        sendBackLongPress = true;
+                    }
                 }
                 mLastLockToAppLongPress = time;
             } else {
                 // If this is back still need to handle sending the long-press event.
-                if (v.getId() == R.id.back) {
+                if (v.getId() == btnId1) {
                     sendBackLongPress = true;
                 } else if (touchExplorationEnabled && inLockTaskMode) {
-                    // When in accessibility mode a long press that is recents (not back)
+                    // When in accessibility mode a long press that is recents/home (not back)
                     // should stop lock task.
                     activityManager.stopSystemLockTaskMode();
                     // When exiting refresh disabled flags.
                     mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
                     return true;
-                } else if (v.getId() == R.id.recent_apps) {
-                    return onLongPressRecents();
+                } else if (v.getId() == btnId2) {
+                    return btnId2 == R.id.recent_apps
+                            ? onLongPressRecents()
+                            : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView());
                 }
             }
             if (sendBackLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index b8b309b..9d20e4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -220,6 +220,11 @@
             newLayout = getDefaultLayout();
         }
         String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+        if (sets.length != 3) {
+            Log.d(TAG, "Invalid layout.");
+            newLayout = getDefaultLayout();
+            sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+        }
         String[] start = sets[0].split(BUTTON_SEPARATOR);
         String[] center = sets[1].split(BUTTON_SEPARATOR);
         String[] end = sets[2].split(BUTTON_SEPARATOR);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index de6ecac..c37dd55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -207,23 +207,6 @@
         }
     }
 
-    private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
-        @Override
-        public void onConnectionChanged(boolean isConnected) {
-            updateSlippery();
-            setDisabledFlags(mDisabledFlags, true);
-            setUpSwipeUpOnboarding(isConnected);
-        }
-
-        @Override
-        public void onRecentsAnimationStarted() {
-            mRecentsAnimationStarted = true;
-            if (mSwipeUpOnboarding != null) {
-                mSwipeUpOnboarding.onRecentsAnimationStarted();
-            }
-        }
-    };
-
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -280,6 +263,19 @@
         notifyVerticalChangedListener(mVertical);
     }
 
+    public void setRecentsAnimationStarted(boolean started) {
+        mRecentsAnimationStarted = started;
+        if (mSwipeUpOnboarding != null) {
+            mSwipeUpOnboarding.onRecentsAnimationStarted();
+        }
+    }
+
+    public void onConnectionChanged(boolean isConnected) {
+        updateSlippery();
+        setDisabledFlags(mDisabledFlags, true);
+        setUpSwipeUpOnboarding(isConnected);
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mGestureHelper.onTouchEvent(event)) {
@@ -304,7 +300,7 @@
                 }
             }
         }
-        return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
+        return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted;
     }
 
     public void abortCurrentGesture() {
@@ -353,6 +349,10 @@
         return mButtonDispatchers;
     }
 
+    public boolean isRecentsButtonVisible() {
+        return getRecentsButton().getVisibility() == View.VISIBLE;
+    }
+
     private void updateCarModeIcons(Context ctx) {
         mBackCarModeIcon = getDrawable(ctx,
                 R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
@@ -613,6 +613,9 @@
         final ViewGroup navbarView = ((ViewGroup) getParent());
         final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView
                 .getLayoutParams();
+        if (lp == null) {
+            return;
+        }
         if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) {
             lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
             changed = true;
@@ -676,6 +679,12 @@
         }
     }
 
+    public void onOverviewProxyConnectionChanged(boolean isConnected) {
+        setSlippery(!isConnected);
+        setDisabledFlags(mDisabledFlags, true);
+        setUpSwipeUpOnboarding(isConnected);
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         mGestureHelper.onDraw(canvas);
@@ -873,7 +882,6 @@
         onPluginDisconnected(null); // Create default gesture helper
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
-        mOverviewProxyService.addCallback(mOverviewProxyListener);
         setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null);
     }
 
@@ -884,7 +892,6 @@
         if (mGestureHelper != null) {
             mGestureHelper.destroy();
         }
-        mOverviewProxyService.removeCallback(mOverviewProxyListener);
         setUpSwipeUpOnboarding(false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 91cae0af..5cf4c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -120,7 +120,6 @@
     private boolean mDisallowNextAnimation;
     private boolean mAnimationsEnabled = true;
     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
-    private int mDarkOffsetX;
     // Keep track of the last visible icon so collapsed container can report on its location
     private IconState mLastVisibleIconState;
 
@@ -378,14 +377,6 @@
                 iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
             }
         }
-
-        if (mDark && mDarkOffsetX != 0) {
-            for (int i = 0; i < childCount; i++) {
-                View view = getChildAt(i);
-                IconState iconState = mIconStates.get(view);
-                iconState.xTranslation += mDarkOffsetX;
-            }
-        }
     }
 
     private float getLayoutEnd() {
@@ -534,10 +525,6 @@
         mAnimationsEnabled = enabled;
     }
 
-    public void setDarkOffsetX(int offsetX) {
-        mDarkOffsetX = offsetX;
-    }
-
     public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
         mReplacingIcons = replacingIcons;
     }
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 31b8159..cd2e77a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -47,6 +47,7 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.keyguard.KeyguardStatusView;
@@ -67,15 +68,14 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.Collection;
 
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener,
@@ -482,7 +482,7 @@
             mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX);
+        mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
         mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
         requestScrollerTopPaddingUpdate(animate);
     }
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
similarity index 70%
rename from services/core/java/com/android/server/am/LockTaskNotify.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
index 1dcb0ad..0d07ad9 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.am;
+package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
 import android.os.SystemClock;
@@ -22,36 +22,37 @@
 import android.view.WindowManager;
 import android.widget.Toast;
 
-import com.android.internal.R;
+import com.android.systemui.R;
+import com.android.systemui.SysUIToast;
 
 /**
  *  Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
  *  pinning mode. All exposed methods should be called from a handler thread.
  */
-public class LockTaskNotify {
-    private static final String TAG = "LockTaskNotify";
+public class ScreenPinningNotify {
+    private static final String TAG = "ScreenPinningNotify";
     private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
 
     private final Context mContext;
     private Toast mLastToast;
     private long mLastShowToastTime;
 
-    public LockTaskNotify(Context context) {
+    public ScreenPinningNotify(Context context) {
         mContext = context;
     }
 
     /** Show "Screen pinned" toast. */
     void showPinningStartToast() {
-        makeAllUserToastAndShow(R.string.lock_to_app_start);
+        makeAllUserToastAndShow(R.string.screen_pinning_start);
     }
 
     /** Show "Screen unpinned" toast. */
     void showPinningExitToast() {
-        makeAllUserToastAndShow(R.string.lock_to_app_exit);
+        makeAllUserToastAndShow(R.string.screen_pinning_exit);
     }
 
     /** Show a toast that describes the gesture the user should use to escape pinned mode. */
-    void showEscapeToast() {
+    void showEscapeToast(boolean isRecentsButtonVisible) {
         long showToastTime = SystemClock.elapsedRealtime();
         if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
             Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -60,14 +61,14 @@
         if (mLastToast != null) {
             mLastToast.cancel();
         }
-        mLastToast = makeAllUserToastAndShow(R.string.lock_to_app_toast);
+        mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible
+                ? R.string.screen_pinning_toast
+                : R.string.screen_pinning_toast_recents_invisible);
         mLastShowToastTime = showToastTime;
     }
 
     private Toast makeAllUserToastAndShow(int resId) {
-        Toast toast = Toast.makeText(mContext, resId, Toast.LENGTH_LONG);
-        toast.getWindowParams().privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        Toast toast = SysUIToast.makeText(mContext, resId, Toast.LENGTH_LONG);
         toast.show();
         return toast;
     }
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 426268b..1bf719a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -404,6 +404,13 @@
     protected NotificationEntryManager mEntryManager;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
 
+    /**
+     * Helper that is responsible for showing the right toast when a disallowed activity operation
+     * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+     * fully locked mode we only show that unlocking is blocked.
+     */
+    private ScreenPinningNotify mScreenPinningNotify;
+
     // for disabling the status bar
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
@@ -830,7 +837,7 @@
         } catch (RemoteException ex) {
             // no window manager? good luck with that
         }
-
+        mScreenPinningNotify = new ScreenPinningNotify(mContext);
         mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
         mStackScroller.setStatusBar(this);
         mStackScroller.setGroupManager(mGroupManager);
@@ -2141,6 +2148,21 @@
 
     }
 
+    @Override
+    public void showPinningEnterExitToast(boolean entering) {
+        if (entering) {
+            mScreenPinningNotify.showPinningStartToast();
+        } else {
+            mScreenPinningNotify.showPinningExitToast();
+        }
+    }
+
+    @Override
+    public void showPinningEscapeToast() {
+        mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
+                || getNavigationBarView().isRecentsButtonVisible());
+    }
+
     boolean panelsEnabled() {
         return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
                 && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index 6a573f5..d85e18c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -14,10 +14,14 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
+import java.util.List;
+
 /**
  * For mocking because AccessibilityManager is final for some reason...
  */
@@ -39,4 +43,27 @@
     public void removeCallback(AccessibilityServicesStateChangeListener listener) {
         mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener);
     }
+
+    public void addAccessibilityStateChangeListener(
+            AccessibilityManager.AccessibilityStateChangeListener listener) {
+        mAccessibilityManager.addAccessibilityStateChangeListener(listener);
+    }
+
+    public void removeAccessibilityStateChangeListener(
+            AccessibilityManager.AccessibilityStateChangeListener listener) {
+        mAccessibilityManager.removeAccessibilityStateChangeListener(listener);
+    }
+
+    public boolean isEnabled() {
+        return mAccessibilityManager.isEnabled();
+    }
+
+    public void sendAccessibilityEvent(AccessibilityEvent event) {
+        mAccessibilityManager.sendAccessibilityEvent(event);
+    }
+
+    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+            int feedbackTypeFlags) {
+        return mAccessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
+    }
 }
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 ad8a0eb..c114a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -365,7 +365,7 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
-    private float mDarkAmount = 1.0f;
+    private float mDarkAmount = 0f;
     private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
             new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
                 @Override
@@ -402,6 +402,7 @@
     private final int mSeparatorThickness;
     private final Rect mTmpRect = new Rect();
     private int mClockBottom;
+    private int mAntiBurnInOffsetX;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -523,9 +524,9 @@
             setClipBounds(null);
         } else {
             float animProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation(mDarkAmount);
+                    .getInterpolation(1f - mDarkAmount);
             float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation(mDarkAmount * 2);
+                    .getInterpolation((1f - mDarkAmount) * 2);
             mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
                     (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
                     (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
@@ -548,7 +549,7 @@
         } else {
             float alpha =
                     BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
-            alpha *= mDarkAmount;
+            alpha *= 1f - mDarkAmount;
             // We need to manually blend in the background color
             int scrimColor = mScrimController.getBackgroundColor();
             color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
@@ -2304,8 +2305,9 @@
             return;
         }
 
+        final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
         mScrimController.setExcludedBackgroundArea(
-                mFadingOut || mParentNotFullyVisible || mDarkAmount != 1 || mIsClipped ? null
+                mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
                         : mCurrentBounds);
         invalidate();
     }
@@ -3858,22 +3860,22 @@
             mDarkNeedsAnimation = true;
             mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
             mNeedsAnimation =  true;
-            setDarkAmount(0.0f);
-        } else if (!dark) {
-            setDarkAmount(1.0f);
-        }
-        requestChildrenUpdate();
-        if (dark) {
-            mScrimController.setExcludedBackgroundArea(null);
         } else {
+            setDarkAmount(dark ? 1f : 0f);
             updateBackground();
         }
-
+        requestChildrenUpdate();
+        applyCurrentBackgroundBounds();
         updateWillNotDraw();
         updateContentHeight();
+        updateAntiBurnInTranslation();
         notifyHeightChangeListener(mShelf);
     }
 
+    private void updateAntiBurnInTranslation() {
+        setTranslationX(mAmbientState.isDark() ? mAntiBurnInOffsetX : 0);
+    }
+
     /**
      * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
      * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
@@ -3894,7 +3896,7 @@
     }
 
     private void startBackgroundFadeIn() {
-        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, 0f, 1f);
+        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, 0f);
         fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
         fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
         fadeAnimator.start();
@@ -4455,8 +4457,9 @@
         mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
     }
 
-    public void setDarkShelfOffsetX(int shelfOffsetX) {
-        mShelf.setDarkOffsetX(shelfOffsetX);
+    public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
+        mAntiBurnInOffsetX = antiBurnInOffsetX;
+        updateAntiBurnInTranslation();
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a166db5..9aee00e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -59,7 +59,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -76,7 +78,7 @@
     private static final int DYNAMIC_STREAM_START_INDEX = 100;
     private static final int VIBRATE_HINT_DURATION = 50;
 
-    private static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
+    static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
     static {
         STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
         STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
@@ -109,8 +111,10 @@
     private boolean mShowVolumeDialog;
     private boolean mShowSafetyWarning;
     private DeviceCallback mDeviceCallback = new DeviceCallback();
-    private AudioDeviceInfo mConnectedDevice;
     private final NotificationManager mNotificationManager;
+    @GuardedBy("mLock")
+    private List<AudioDeviceInfo> mConnectedDevices = new ArrayList<>();
+    private Object mLock = new Object();
 
     private boolean mDestroyed;
     private VolumePolicy mVolumePolicy;
@@ -1055,26 +1059,25 @@
 
     protected final class DeviceCallback extends AudioDeviceCallback {
         public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
-            for (AudioDeviceInfo info : addedDevices) {
-                if (info.isSink()
-                        && (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
-                        || info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO)) {
-                    mConnectedDevice = info;
-                    mCallbacks.onConnectedDeviceChanged(info.getProductName().toString());
+            synchronized (mLock) {
+                for (AudioDeviceInfo info : addedDevices) {
+                    if (info.isSink()
+                            && (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
+                            || info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO)) {
+                        mConnectedDevices.add(info);
+                        mCallbacks.onConnectedDeviceChanged(info.getProductName().toString());
+                    }
                 }
             }
         }
 
         public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
-            if (mConnectedDevice == null) {
-                mCallbacks.onConnectedDeviceChanged(null);
-                return;
-            }
-            for (AudioDeviceInfo info : removedDevices) {
-                if (info.isSink() == mConnectedDevice.isSink()
-                        && Objects.equals(info.getProductName(), mConnectedDevice.getProductName())
-                        && info.getType() == mConnectedDevice.getType()) {
-                    mConnectedDevice = null;
+            synchronized (mLock) {
+                for (AudioDeviceInfo info : removedDevices) {
+                    mConnectedDevices.remove(info);
+                }
+
+                if (mConnectedDevices.size() == 0) {
                     mCallbacks.onConnectedDeviceChanged(null);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 8cfdeeb6..0c6e0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -36,7 +36,6 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -58,12 +57,10 @@
 import android.view.View.AccessibilityDelegate;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
-import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageButton;
@@ -79,6 +76,7 @@
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -105,13 +103,14 @@
     private CustomDialog mDialog;
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
+    private ViewGroup mFooter;
     private ImageButton mRingerIcon;
     private TextView mRingerStatus;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
-    private final AccessibilityManager mAccessibilityMgr;
+    private final AccessibilityManagerWrapper mAccessibilityMgr;
     private final Object mSafetyWarningLock = new Object();
     private final Object mOutputChooserLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
@@ -134,8 +133,7 @@
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mController = Dependency.get(VolumeDialogController.class);
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mAccessibilityMgr =
-                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
         mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
         mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
     }
@@ -203,8 +201,9 @@
         hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
 
         mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
-        mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
-        mRingerStatus = mDialog.findViewById(R.id.ringer_status);
+        mFooter = mDialog.findViewById(R.id.footer);
+        mRingerIcon = mFooter.findViewById(R.id.ringer_icon);
+        mRingerStatus = mFooter.findViewById(R.id.ringer_status);
 
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
@@ -231,6 +230,10 @@
         initRingerH();
     }
 
+    protected ViewGroup getDialogView() {
+        return mDialogView;
+    }
+
     private ColorStateList loadColorStateList(int colorResId) {
         return ColorStateList.valueOf(mContext.getColor(colorResId));
     }
@@ -258,6 +261,7 @@
 
     private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
             boolean defaultStream, boolean dynamic) {
+        if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
         VolumeRow row = new VolumeRow();
         initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
         int rowSize;
@@ -336,36 +340,8 @@
 
         row.outputChooser = row.view.findViewById(R.id.output_chooser);
         row.outputChooser.setOnClickListener(mClickOutputChooser);
-        row.outputChooser.findViewById(R.id.output_chooser_button)
-                .setOnClickListener(mClickOutputChooser);
         row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device);
 
-        // forward events above the slider into the slider
-        row.view.findViewById(R.id.volume_row_slider_frame)
-                .setOnTouchListener(new OnTouchListener() {
-            private final Rect mSliderHitRect = new Rect();
-            private boolean mDragging;
-
-            @SuppressLint("ClickableViewAccessibility")
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                row.slider.getHitRect(mSliderHitRect);
-                if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN
-                        && event.getY() < mSliderHitRect.top) {
-                    mDragging = true;
-                }
-                if (mDragging) {
-                    event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top);
-                    row.slider.dispatchTouchEvent(event);
-                    if (event.getActionMasked() == MotionEvent.ACTION_UP
-                            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
-                        mDragging = false;
-                    }
-                    return true;
-                }
-                return false;
-            }
-        });
         row.icon = row.view.findViewById(R.id.volume_row_icon);
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
@@ -408,6 +384,8 @@
             if (ss == null) {
                 return;
             }
+            // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
+            // a vibrator.
             final boolean hasVibrator = mController.hasVibrator();
             if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                 if (hasVibrator) {
@@ -415,7 +393,12 @@
                 } else {
                     final boolean wasZero = ss.level == 0;
                     mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+                    mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
                 }
+            } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+                final boolean wasZero = ss.level == 0;
+                mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+                mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
             } else {
                 mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
                 if (ss.level == 0) {
@@ -621,7 +604,7 @@
         }
     }
 
-    private void onStateChangedH(State state) {
+    protected void onStateChangedH(State state) {
         mState = state;
         mDynamic.clear();
         // add any new dynamic rows
@@ -894,7 +877,7 @@
             return ss.remoteLabel;
         }
         try {
-            return mContext.getString(ss.name);
+            return mContext.getResources().getString(ss.name);
         } catch (Resources.NotFoundException e) {
             Slog.e(TAG, "Can't find translation for stream " + ss);
             return "";
@@ -904,7 +887,6 @@
     private final OnClickListener mClickOutputChooser = new OnClickListener() {
         @Override
         public void onClick(View v) {
-            // TODO: log
             dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
             showOutputChooserH();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 368194e..4c69594 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -133,11 +133,11 @@
         for (int i = 0; i < rowCount; i++) {
             View row = rows.getChildAt(i);
             if (to == ROTATION_SEASCAPE) {
-                rotateSeekBars(row, to, 180);
-            } else if (to == ROTATION_LANDSCAPE) {
                 rotateSeekBars(row, to, 0);
+            } else if (to == ROTATION_LANDSCAPE) {
+                rotateSeekBars(row, to, 180);
             } else {
-                rotateSeekBars(row, to, 270);
+                rotateSeekBars(row, to, 90);
             }
             rotate(row, from, to, true);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
similarity index 66%
rename from packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 2a44771..2f05b06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -14,9 +14,13 @@
 
 package com.android.systemui;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -29,6 +33,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Fragment;
+import android.content.res.Configuration;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.view.Display;
@@ -36,7 +41,7 @@
 import android.view.WindowManager;
 
 import com.android.systemui.R.dimen;
-import com.android.systemui.RoundedCorners.TunablePaddingTagListener;
+import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -51,9 +56,9 @@
 
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class RoundedCornersTest extends SysuiTestCase {
+public class ScreenDecorationsTest extends SysuiTestCase {
 
-    private RoundedCorners mRoundedCorners;
+    private ScreenDecorations mScreenDecorations;
     private StatusBar mStatusBar;
     private WindowManager mWindowManager;
     private FragmentService mFragmentService;
@@ -81,20 +86,22 @@
 
         mTunerService = mDependency.injectMockDependency(TunerService.class);
 
-        mRoundedCorners = new RoundedCorners();
-        mRoundedCorners.mContext = mContext;
-        mRoundedCorners.mComponents = mContext.getComponents();
+        mScreenDecorations = new ScreenDecorations();
+        mScreenDecorations.mContext = mContext;
+        mScreenDecorations.mComponents = mContext.getComponents();
 
         mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
     }
 
     @Test
-    public void testNoRounding() {
+    public void testNoRounding_NoCutout() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
         mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
 
-        mRoundedCorners.start();
+        mScreenDecorations.start();
         // No views added.
         verify(mWindowManager, never()).addView(any(), any());
         // No Fragments watched.
@@ -105,11 +112,13 @@
 
     @Test
     public void testRounding() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
         mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
 
-        mRoundedCorners.start();
+        mScreenDecorations.start();
         // Add 2 windows for rounded corners (top and bottom).
         verify(mWindowManager, times(2)).addView(any(), any());
 
@@ -122,6 +131,44 @@
     }
 
     @Test
+    public void testCutout() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
+        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 0);
+
+        mScreenDecorations.start();
+        // Add 2 windows for rounded corners (top and bottom).
+        verify(mWindowManager, times(2)).addView(any(), any());
+    }
+
+    @Test
+    public void testDelayedCutout() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 0);
+
+        mScreenDecorations.start();
+
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
+        mScreenDecorations.onConfigurationChanged(new Configuration());
+
+        // Add 2 windows for rounded corners (top and bottom).
+        verify(mWindowManager, times(2)).addView(any(), any());
+    }
+
+    @Test
+    public void hasRoundedCornerOverlayFlagSet() {
+        assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
+                        & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+                is(PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY));
+    }
+
+    @Test
     public void testPaddingTagListener() {
         TunablePaddingTagListener tagListener = new TunablePaddingTagListener(14, 5);
         View v = mock(View.class);
@@ -136,7 +183,7 @@
 
         // Trigger callback and verify we get a TunablePadding created.
         tagListener.onFragmentViewCreated(null, f);
-        verify(mTunablePaddingService).add(eq(child), eq(RoundedCorners.PADDING), eq(14),
+        verify(mTunablePaddingService).add(eq(child), eq(ScreenDecorations.PADDING), eq(14),
                 eq(FLAG_START | FLAG_END));
 
         // Call again and verify destroy is called.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 40f8059..dfc1852 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -30,6 +30,7 @@
 import com.android.systemui.Prefs.Key;
 import com.android.systemui.SysuiTestCase;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,6 +41,11 @@
 
     private AutoAddTracker mAutoTracker;
 
+    @Before
+    public void setUp() {
+        Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, "");
+    }
+
     @Test
     public void testMigration() {
         Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
@@ -50,7 +56,10 @@
         assertTrue(mAutoTracker.isAdded(WORK));
         assertFalse(mAutoTracker.isAdded(INVERSION));
 
+        // These keys have been removed; retrieving their values should always return the default.
+        assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true ));
         assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false));
+        assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true));
         assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false));
 
         mAutoTracker.destroy();
@@ -96,5 +105,4 @@
 
         mAutoTracker.destroy();
     }
-
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index a95e3db..2d2db1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -18,45 +18,48 @@
 
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager.AlarmClockInfo;
+import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
-
 import com.android.internal.app.ColorDisplayController;
 import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
-
-import org.junit.After;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
 public class AutoTileManagerTest extends SysuiTestCase {
 
-    private QSTileHost mQsTileHost;
+    @Mock private QSTileHost mQsTileHost;
+    @Mock private AutoAddTracker mAutoAddTracker;
+    @Captor private ArgumentCaptor<NextAlarmChangeCallback> mAlarmCallback;
+
     private AutoTileManager mAutoTileManager;
 
     @Before
     public void setUp() throws Exception {
-        mDependency.injectTestDependency(Dependency.BG_LOOPER,
-                TestableLooper.get(this).getLooper());
-        Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
-        mQsTileHost = Mockito.mock(QSTileHost.class);
-        mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mAutoTileManager = null;
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(NextAlarmController.class);
+        mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker,
+            mQsTileHost, new Handler(TestableLooper.get(this).getLooper()));
+        verify(Dependency.get(NextAlarmController.class))
+            .addCallback(mAlarmCallback.capture());
     }
 
     @Test
@@ -106,4 +109,30 @@
                 ColorDisplayController.AUTO_MODE_DISABLED);
         verify(mQsTileHost, never()).addTile("night");
     }
+
+    @Test
+    public void alarmTileAdded_whenAlarmSet() {
+        mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
+
+        verify(mQsTileHost).addTile("alarm");
+        verify(mAutoAddTracker).setTileAdded("alarm");
+    }
+
+    @Test
+    public void alarmTileNotAdded_whenAlarmNotSet() {
+        mAlarmCallback.getValue().onNextAlarmChanged(null);
+
+        verify(mQsTileHost, never()).addTile("alarm");
+        verify(mAutoAddTracker, never()).setTileAdded("alarm");
+    }
+
+    @Test
+    public void alarmTileNotAdded_whenAlreadyAdded() {
+        when(mAutoAddTracker.isAdded("alarm")).thenReturn(true);
+
+        mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
+
+        verify(mQsTileHost, never()).addTile("alarm");
+        verify(mAutoAddTracker, never()).setTileAdded("alarm");
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index 1c010b6..d9673d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -61,4 +61,15 @@
         Assert.assertFalse(mStackScroller.isDimmed());
     }
 
+    @Test
+    public void testAntiBurnInOffset() {
+        final int burnInOffset = 30;
+        mStackScroller.setAntiBurnInOffsetX(burnInOffset);
+        mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */);
+        Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
+        mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */);
+        Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
+                0.01 /* delta */);
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index c18ed73..f7bb065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -44,11 +44,13 @@
 
 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;
 
+@Ignore
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -87,7 +89,7 @@
     public void tearDown() throws Exception {
         TestableLooper.get(this).processAllMessages();
     }
-
+/*
     @Test
     public void testClickMediaRouterItemConnectsMedia() {
         mDialog.show();
@@ -137,7 +139,7 @@
                 .getText().toString().contains("Phone"));
         mDialog.dismiss();
     }
-
+*/
     @Test
     public void testNoMediaScanIfInCall() {
         mDialog.setIsInCall(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
new file mode 100644
index 0000000..43d60e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_RING;
+
+import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
+import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
+import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.media.AudioManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.VolumeDialogController.State;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+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 java.util.function.Predicate;
+
+@Ignore
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class VolumeDialogImplTest extends SysuiTestCase {
+
+    VolumeDialogImpl mDialog;
+
+    @Mock
+    VolumeDialogController mController;
+
+    @Mock
+    KeyguardManager mKeyguard;
+
+    @Mock
+    AccessibilityManagerWrapper mAccessibilityMgr;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mController = mDependency.injectMockDependency(VolumeDialogController.class);
+        mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
+        getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
+
+        mDialog = new VolumeDialogImpl(getContext());
+        mDialog.init(0, null);
+        State state = createShellState();
+        mDialog.onStateChangedH(state);
+    }
+
+    private State createShellState() {
+        State state = new VolumeDialogController.State();
+        for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
+            VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
+            ss.name = STREAMS.get(i);
+            ss.level = 1;
+            state.states.append(i, ss);
+        }
+        return state;
+    }
+
+    private void navigateViews(View view, Predicate<View> condition) {
+        if (view instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) view;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                navigateViews(viewGroup.getChildAt(i), condition);
+            }
+        } else {
+            String resourceName = null;
+            try {
+                resourceName = getContext().getResources().getResourceName(view.getId());
+            } catch (Exception e) {}
+            assertTrue("View " + resourceName != null ? resourceName : view.getId()
+                    + " failed test", condition.test(view));
+        }
+    }
+/*
+    @Test
+    public void testContentDescriptions() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        navigateViews(dialog, view -> {
+            if (view instanceof ImageView) {
+                return !TextUtils.isEmpty(view.getContentDescription());
+            } else {
+                return true;
+            }
+        });
+
+        mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+    }
+
+    @Test
+    public void testNoDuplicationOfParentState() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled());
+
+        mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+    }
+
+    @Test
+    public void testNoClickableViewGroups() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        navigateViews(dialog, view -> {
+            if (view instanceof ViewGroup) {
+                return !view.isClickable();
+            } else {
+                return true;
+            }
+        });
+
+        mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+    }
+
+    @Test
+    public void testTristateToggle_withVibrator() {
+        when(mController.hasVibrator()).thenReturn(true);
+
+        State state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(state);
+
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        // click once, verify updates to vibrate
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_VIBRATE;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to silent
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.states.get(STREAM_RING).level = 0;
+        state.ringerModeInternal = RINGER_MODE_SILENT;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to normal
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+    }
+
+    @Test
+    public void testTristateToggle_withoutVibrator() {
+        when(mController.hasVibrator()).thenReturn(false);
+
+        State state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(state);
+
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        // click once, verify updates to silent
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.states.get(STREAM_RING).level = 0;
+        state.ringerModeInternal = RINGER_MODE_SILENT;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to normal
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+    }
+    */
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
deleted file mode 100644
index 4ab2063..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
+++ /dev/null
@@ -1,220 +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.
- */
-
-package com.android.systemui.volume;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.Uri;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.FlakyTest;
-import android.view.LayoutInflater;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ZenModePanelTest extends SysuiTestCase {
-
-    ZenModePanel mPanel;
-    ZenModeController mController;
-    Uri mForeverId;
-
-    @Before
-    public void setup() throws Exception {
-        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        mPanel = (ZenModePanel) layoutInflater.inflate(com.android.systemui.R.layout.zen_mode_panel,
-                null);
-        mController = mock(ZenModeController.class);
-        mForeverId = Condition.newId(mContext).appendPath("forever").build();
-
-        mPanel.init(mController);
-    }
-
-    private void assertProperConditionTagTypes(boolean hasAlarm) {
-        final int N = mPanel.getVisibleConditions();
-        assertEquals(hasAlarm ? 3 : 2, N);
-
-        assertEquals(mForeverId, mPanel.getConditionTagAt(0).condition.id);
-        assertTrue(ZenModeConfig.isValidCountdownConditionId(
-                mPanel.getConditionTagAt(1).condition.id));
-        assertFalse(ZenModeConfig.isValidCountdownToAlarmConditionId(
-                mPanel.getConditionTagAt(1).condition.id));
-        if (hasAlarm) {
-            assertTrue(ZenModeConfig.isValidCountdownToAlarmConditionId(
-                    mPanel.getConditionTagAt(2).condition.id));
-        }
-    }
-
-    @Test
-    public void testHandleUpdateConditions_foreverSelected_alarmExists() {
-         Condition forever = new Condition(mForeverId, "", Condition.STATE_TRUE);
-
-        when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
-        mPanel.handleUpdateConditions(forever);
-        assertProperConditionTagTypes(true);
-        assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
-    }
-
-    @Test
-    public void testHandleUpdateConditions_foreverSelected_noAlarm() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-        Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
-        when(mController.getNextAlarm()).thenReturn((long) 0);
-
-        mPanel.handleUpdateConditions(forever);
-        assertProperConditionTagTypes(false);
-        assertEquals(foreverId, mPanel.getConditionTagAt(0).condition.id);
-    }
-
-    @Test
-    public void testHandleUpdateConditions_countdownSelected_alarmExists() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
-        Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
-                "", Condition.STATE_TRUE);
-
-        when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
-        mPanel.handleUpdateConditions(countdown);
-        assertProperConditionTagTypes(true);
-        assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
-    }
-
-    @Test
-    public void testHandleUpdateConditions_countdownSelected_noAlarm() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
-        Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
-                "", Condition.STATE_TRUE);
-
-        when(mController.getNextAlarm()).thenReturn((long) 0);
-
-        mPanel.handleUpdateConditions(countdown);
-        assertProperConditionTagTypes(false);
-        assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
-    }
-
-    @Test
-    public void testHandleUpdateConditions_nextAlarmSelected() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
-        Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + 1000, true),
-                "", Condition.STATE_TRUE);
-        when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 9000);
-
-        mPanel.handleUpdateConditions(alarm);
-
-        assertProperConditionTagTypes(true);
-        assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
-        assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-    }
-
-    @Test
-    public void testHandleUpdateConditions_foreverSelected_alarmConditionDoesNotChangeIfAttached() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-        Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
-        Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + 9000, true),
-                "", Condition.STATE_TRUE);
-        when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
-        mPanel.handleUpdateConditions(alarm);
-        mPanel.setAttached(true);
-        mPanel.handleUpdateConditions(forever);
-
-        assertProperConditionTagTypes(true);
-        assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
-        assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
-    }
-
-    @Test
-    public void testHandleUpdateConditions_foreverSelected_timeConditionDoesNotChangeIfAttached() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-        Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
-        Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
-                "", Condition.STATE_TRUE);
-        when(mController.getNextAlarm()).thenReturn((long) 0);
-
-        mPanel.handleUpdateConditions(countdown);
-        mPanel.setAttached(true);
-        mPanel.handleUpdateConditions(forever);
-
-        assertProperConditionTagTypes(false);
-        assertEquals(countdown, mPanel.getConditionTagAt(1).condition);
-        assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testHandleUpdateManualRule_stillSelectedAfterModeChange() {
-        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-
-        Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
-                System.currentTimeMillis() + 1000, true),
-                "", Condition.STATE_TRUE);
-
-        rule.condition = alarm;
-        rule.conditionId = alarm.id;
-        rule.enabled = true;
-        rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-
-        mPanel.handleUpdateManualRule(rule);
-
-        assertProperConditionTagTypes(true);
-        assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
-        assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
-        assertEquals(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
-
-        rule.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
-
-        mPanel.handleUpdateManualRule(rule);
-
-        assertProperConditionTagTypes(true);
-        assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
-        assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
-        assertEquals(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
-                mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
-    }
-}
diff --git a/proto/src/gnss.proto b/proto/src/gnss.proto
index c54ddad..0168392 100644
--- a/proto/src/gnss.proto
+++ b/proto/src/gnss.proto
@@ -42,4 +42,20 @@
 
   // Standard deviation of top 4 average CN0 (dB-Hz)
   optional double standard_deviation_top_four_average_cn0_db_hz = 11;
-}
\ No newline at end of file
+
+  // Power metrics
+  optional PowerMetrics power_metrics = 12;
+}
+
+// Power metrics
+message PowerMetrics {
+
+  // Duration of power log (ms)
+  optional int64 logging_duration_ms = 1;
+
+  // Energy consumed (mAh)
+  optional double energy_consumed_mah = 2;
+
+  // Time spent in signal quality level (ms)
+  repeated int64 time_in_signal_quality_level_ms = 3;
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 03dfd46..01714cf 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5168,6 +5168,23 @@
     // OS: P
     ROTATION_SUGGESTION_SHOWN = 1288;
 
+    // An autofill service was bound using an unofficial(but still supported) permission.
+    // Package: Package of the autofill service
+    // OS: P
+    AUTOFILL_INVALID_PERMISSION = 1289;
+
+    // OPEN: QS Alarm tile shown
+    // ACTION: QS Alarm tile tapped
+    //  SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: QUICK_SETTINGS
+    // OS: P
+    QS_ALARM = 1290;
+
+    // OPEN: Settings->Connected Devices->USB->(click on details link)
+    // CATEGORY: SETTINGS
+    // OS: P
+    USB_DEVICE_DETAILS = 1291;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index db70184..08fdb97 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -200,6 +200,10 @@
     // Package: android
     NOTE_CARRIER_NETWORK_AVAILABLE = 46;
 
+    // Inform that USB is configured for Tethering
+    // Package: android
+    NOTE_USB_TETHER = 47;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f5349df..c77dcc0 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -63,7 +63,7 @@
   // Number scans that returned at least one result.
   optional int32 num_non_empty_scan_results = 13;
 
-  // Number of scans that were one time.
+  // Number of single scans requests.
   optional int32 num_oneshot_scans = 14;
 
   // Number of repeated background scans that were scheduled to the chip.
@@ -373,6 +373,12 @@
 
   // Wps connection metrics
   optional WpsMetrics wps_metrics = 91;
+
+  // Wifi power statistics
+  optional WifiPowerStats wifi_power_stats = 92;
+
+  // Number of connectivity single scan requests.
+  optional int32 num_connectivity_oneshot_scans = 93;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -1134,3 +1140,22 @@
   // Total number of wps cancellation
   optional int32 num_wps_cancellation = 8;
 }
+
+// Power stats for Wifi
+message WifiPowerStats {
+
+  // Duration of log (ms)
+  optional int64 logging_duration_ms = 1;
+
+  // Energy consumed by wifi (mAh)
+  optional double energy_consumed_mah = 2;
+
+  // Amount of time wifi is in idle (ms)
+  optional int64 idle_time_ms = 3;
+
+  // Amount of time wifi is in rx (ms)
+  optional int64 rx_time_ms = 4;
+
+  // Amount of time wifi is in tx (ms)
+  optional int64 tx_time_ms = 5;
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 0e2ca14..2b73b33 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -617,6 +617,23 @@
         }
 
         @Override
+        public String getUserDataId() throws RemoteException {
+            final int userId = UserHandle.getCallingUserId();
+
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    final UserData userData = service.getUserData(getCallingUid());
+                    return userData == null ? null : userData.getId();
+                } else if (sVerbose) {
+                    Slog.v(TAG, "getUserDataId(): no service for " + userId);
+                }
+            }
+
+            return null;
+        }
+
+        @Override
         public void setUserData(UserData userData) throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6b44fa5..6108afa 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -111,7 +111,7 @@
  * until the user authenticates or it times out.
  */
 final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
-        AutoFillUI.AutoFillUiCallback {
+        AutoFillUI.AutoFillUiCallback, ValueFinder {
     private static final String TAG = "AutofillSession";
 
     private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
@@ -310,43 +310,56 @@
         return ids;
     }
 
-    /**
-     * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or
-     * {@code null} when not found on either of them.
-     */
+    @Override
     @Nullable
-    private String getValueAsString(@NonNull AutofillId id) {
-        AutofillValue value = null;
+    public String findByAutofillId(@NonNull AutofillId id) {
         synchronized (mLock) {
-            final ViewState state = mViewStates.get(id);
-            if (state == null) {
-                if (sDebug) Slog.d(TAG, "getValue(): no view state for " + id);
-                return null;
-            }
-            value = state.getCurrentValue();
-            if (value == null) {
-                if (sDebug) Slog.d(TAG, "getValue(): no current value for " + id);
-                value = getValueFromContextsLocked(id);
-            }
-        }
-        if (value != null) {
-            if (value.isText()) {
-                return value.getTextValue().toString();
-            }
-            if (value.isList()) {
-                final CharSequence[] options = getAutofillOptionsFromContextsLocked(id);
-                if (options != null) {
-                    final int index = value.getListValue();
-                    final CharSequence option = options[index];
-                    return option != null ? option.toString() : null;
-                } else {
-                    Slog.w(TAG, "getValueAsString(): no autofill options for id " + id);
+            AutofillValue value = findValueLocked(id);
+            if (value != null) {
+                if (value.isText()) {
+                    return value.getTextValue().toString();
+                }
+
+                if (value.isList()) {
+                    final CharSequence[] options = getAutofillOptionsFromContextsLocked(id);
+                    if (options != null) {
+                        final int index = value.getListValue();
+                        final CharSequence option = options[index];
+                        return option != null ? option.toString() : null;
+                    } else {
+                        Slog.w(TAG, "findByAutofillId(): no autofill options for id " + id);
+                    }
                 }
             }
         }
         return null;
     }
 
+    @Override
+    public AutofillValue findRawValueByAutofillId(AutofillId id) {
+        synchronized (mLock) {
+            return findValueLocked(id);
+        }
+    }
+
+    /**
+     * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
+     * or {@code null} when not found on either of them.
+     */
+    private AutofillValue findValueLocked(@NonNull AutofillId id) {
+        final ViewState state = mViewStates.get(id);
+        if (state == null) {
+            if (sDebug) Slog.d(TAG, "findValueLocked(): no view state for " + id);
+            return null;
+        }
+        AutofillValue value = state.getCurrentValue();
+        if (value == null) {
+            if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + id);
+            value = getValueFromContextsLocked(id);
+        }
+        return value;
+    }
+
     /**
      * Updates values of the nodes in the context's structure so that:
      * - proper node is focused
@@ -1355,14 +1368,12 @@
                 if (sDebug) {
                     Slog.d(TAG, "at least one field changed, validate fields for save UI");
                 }
-                final ValueFinder valueFinder = (id) -> {return getValueAsString(id);};
-
                 final InternalValidator validator = saveInfo.getValidator();
                 if (validator != null) {
                     final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_VALIDATION);
                     boolean isValid;
                     try {
-                        isValid = validator.isValid(valueFinder);
+                        isValid = validator.isValid(this);
                         if (sDebug) Slog.d(TAG, validator + " returned " + isValid);
                         log.setType(isValid
                                 ? MetricsEvent.TYPE_SUCCESS
@@ -1404,10 +1415,13 @@
                             }
                             final AutofillValue datasetValue = datasetValues.get(id);
                             if (!currentValue.equals(datasetValue)) {
-                                if (sDebug) Slog.d(TAG, "found a change on id " + id);
+                                if (sDebug) {
+                                    Slog.d(TAG, "found a dataset change on id " + id + ": from "
+                                            + datasetValue + " to " + currentValue);
+                                }
                                 continue datasets_loop;
                             }
-                            if (sVerbose) Slog.v(TAG, "no changes for id " + id);
+                            if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id);
                         }
                         if (sDebug) {
                             Slog.d(TAG, "ignoring Save UI because all fields match contents of "
@@ -1425,7 +1439,7 @@
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(mActivityToken, id, client);
                 getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
-                        mService.getServicePackageName(), saveInfo, valueFinder,
+                        mService.getServicePackageName(), saveInfo, this,
                         mComponentName.getPackageName(), this,
                         mPendingSaveUi);
                 if (client != null) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5ee3cbf..5f112c7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -27,6 +27,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.service.autofill.Dataset;
+import android.service.autofill.Dataset.DatasetFieldFilter;
 import android.service.autofill.FillResponse;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -58,6 +59,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 final class FillUi {
     private static final String TAG = "FillUi";
@@ -185,7 +187,7 @@
             final ArrayList<ViewItem> items = new ArrayList<>(totalItems);
             if (header != null) {
                 if (sVerbose) Slog.v(TAG, "adding header");
-                items.add(new ViewItem(null, null, null, header));
+                items.add(new ViewItem(null, null, false, null, header));
             }
             for (int i = 0; i < datasetCount; i++) {
                 final Dataset dataset = response.getDatasets().get(i);
@@ -205,21 +207,32 @@
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
                     }
-                    final Pattern filter = dataset.getFilter(index);
+                    final DatasetFieldFilter filter = dataset.getFilter(index);
+                    Pattern filterPattern = null;
                     String valueText = null;
+                    boolean filterable = true;
                     if (filter == null) {
                         final AutofillValue value = dataset.getFieldValues().get(index);
                         if (value != null && value.isText()) {
                             valueText = value.getTextValue().toString().toLowerCase();
                         }
+                    } else {
+                        filterPattern = filter.pattern;
+                        if (filterPattern == null) {
+                            if (sVerbose) {
+                                Slog.v(TAG, "Explicitly disabling filter at id " + focusedViewId
+                                        + " for dataset #" + index);
+                            }
+                            filterable = false;
+                        }
                     }
 
-                    items.add(new ViewItem(dataset, filter, valueText, view));
+                    items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
                 }
             }
             if (footer != null) {
                 if (sVerbose) Slog.v(TAG, "adding footer");
-                items.add(new ViewItem(null, null, null, footer));
+                items.add(new ViewItem(null, null, false, null, footer));
             }
 
             mAdapter = new ItemsAdapter(items);
@@ -354,7 +367,7 @@
                 MeasureSpec.AT_MOST);
         final int itemCount = mAdapter.getCount();
         for (int i = 0; i < itemCount; i++) {
-            View view = mAdapter.getItem(i).view;
+            final View view = mAdapter.getItem(i).view;
             view.measure(widthMeasureSpec, heightMeasureSpec);
             final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
             final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
@@ -400,13 +413,62 @@
         public final @Nullable Dataset dataset;
         public final @NonNull View view;
         public final @Nullable Pattern filter;
+        public final boolean filterable;
 
-        ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, @Nullable String value,
-                @NonNull View view) {
+        /**
+         * Default constructor.
+         *
+         * @param dataset dataset associated with the item or {@code null} if it's a header or
+         * footer (TODO(b/69796626): make @NonNull if header/footer is refactored out of the list)
+         * @param filter optional filter set by the service to determine how the item should be
+         * filtered
+         * @param filterable optional flag set by the service to indicate this item should not be
+         * filtered (typically used when the dataset has value but it's sensitive, like a password)
+         * @param value dataset value
+         * @param view dataset presentation.
+         */
+        ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, boolean filterable,
+                @Nullable String value, @NonNull View view) {
             this.dataset = dataset;
             this.value = value;
             this.view = view;
             this.filter = filter;
+            this.filterable = filterable;
+        }
+
+        /**
+         * Returns whether this item matches the value input by the user so it can be included
+         * in the filtered datasets.
+         */
+        public boolean matches(CharSequence filterText) {
+            if (TextUtils.isEmpty(filterText)) {
+                // Always show item when the user input is empty
+                return true;
+            }
+            if (!filterable) {
+                // Service explicitly disabled filtering using a null Pattern.
+                return false;
+            }
+            final String constraintLowerCase = filterText.toString().toLowerCase();
+            if (filter != null) {
+                // Uses pattern provided by service
+                return filter.matcher(constraintLowerCase).matches();
+            } else {
+                // Compares it with dataset value with dataset
+                return (value == null)
+                        ? (dataset.getAuthentication() == null)
+                        : value.toLowerCase().startsWith(constraintLowerCase);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId())
+                    + ", value=" + (value == null ? "null" : value.length() + "_chars")
+                    + ", filterable=" + filterable
+                    + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars")
+                    + ", view=" + view.getAutofillId()
+                    + "]";
         }
     }
 
@@ -509,7 +571,7 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
         pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
-        pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null);
+        pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
         pw.print(prefix); pw.print("mFilterText: ");
         Helper.printlnRedactedText(pw, mFilterText);
         pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
@@ -556,33 +618,14 @@
         public Filter getFilter() {
             return new Filter() {
                 @Override
-                protected FilterResults performFiltering(CharSequence constraint) {
+                protected FilterResults performFiltering(CharSequence filterText) {
                     // No locking needed as mAllItems is final an immutable
+                    final List<ViewItem> filtered = mAllItems.stream()
+                            .filter((item) -> item.matches(filterText))
+                            .collect(Collectors.toList());
                     final FilterResults results = new FilterResults();
-                    if (TextUtils.isEmpty(constraint)) {
-                        results.values = mAllItems;
-                        results.count = mAllItems.size();
-                        return results;
-                    }
-                    final List<ViewItem> filteredItems = new ArrayList<>();
-                    final String constraintLowerCase = constraint.toString().toLowerCase();
-                    final int itemCount = mAllItems.size();
-                    for (int i = 0; i < itemCount; i++) {
-                        final ViewItem item = mAllItems.get(i);
-                        final boolean matches;
-                        if (item.filter != null) {
-                            matches = item.filter.matcher(constraintLowerCase).matches();
-                        } else {
-                            matches = (item.value == null)
-                                    ? (item.dataset.getAuthentication() == null)
-                                    : item.value.toLowerCase().startsWith(constraintLowerCase);
-                        }
-                        if (matches) {
-                            filteredItems.add(item);
-                        }
-                    }
-                    results.values = filteredItems;
-                    results.count = filteredItems.size();
+                    results.values = filtered;
+                    results.count = filtered.size();
                     return results;
                 }
 
@@ -624,6 +667,11 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             return getItem(position).view;
         }
+
+        @Override
+        public String toString() {
+            return "ItemsAdapter: [all=" + mAllItems + ", filtered=" + mFilteredItems + "]";
+        }
     }
 
     private final class AnnounceFilterResult implements Runnable {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 342b48e..30dfee8 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
@@ -26,6 +27,8 @@
 import android.app.IAlarmManager;
 import android.app.IUidObserver;
 import android.app.PendingIntent;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -57,6 +60,7 @@
 import android.util.ArrayMap;
 import android.util.KeyValueListParser;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -75,6 +79,7 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Random;
@@ -120,6 +125,7 @@
     static final boolean DEBUG_LISTENER_CALLBACK = localLOGV || false;
     static final boolean DEBUG_WAKELOCK = localLOGV || false;
     static final boolean DEBUG_BG_LIMIT = localLOGV || false;
+    static final boolean DEBUG_STANDBY = localLOGV || false;
     static final boolean RECORD_ALARMS_IN_HISTORY = true;
     static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
     static final int ALARM_EVENT = 1;
@@ -140,6 +146,7 @@
 
     AppOpsManager mAppOps;
     DeviceIdleController.LocalService mLocalDeviceIdleController;
+    private UsageStatsManagerInternal mUsageStatsManagerInternal;
 
     final Object mLock = new Object();
 
@@ -215,6 +222,14 @@
     }
     final ArrayList<IdleDispatchEntry> mAllowWhileIdleDispatches = new ArrayList();
 
+    interface Stats {
+        int REBATCH_ALL_ALARMS = 0;
+    }
+
+    private final StatLogger mStatLogger = new StatLogger(new String[] {
+            "REBATCH_ALL_ALARMS",
+    });
+
     /**
      * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
      */
@@ -233,6 +248,8 @@
             new SparseArray<>();
 
     private final ForceAppStandbyTracker mForceAppStandbyTracker;
+    private boolean mAppStandbyParole;
+    private ArrayMap<Pair<String, Integer>, Long> mLastAlarmDeliveredForPackage = new ArrayMap<>();
 
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
@@ -249,13 +266,28 @@
                 = "allow_while_idle_whitelist_duration";
         private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
 
+        // Keys for specifying throttling delay based on app standby bucketing
+        private final String[] KEYS_APP_STANDBY_DELAY = {
+                "standby_active_delay",
+                "standby_working_delay",
+                "standby_frequent_delay",
+                "standby_rare_delay",
+                "standby_never_delay",
+        };
+
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
-
         private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
+        private final long[] DEFAULT_APP_STANDBY_DELAYS = {
+                0,                       // Active
+                6 * 60_000,              // Working
+                30 * 60_000,             // Frequent
+                2 * 60 * 60_000,         // Rare
+                10 * 24 * 60 * 60_000    // Never
+        };
 
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -276,6 +308,8 @@
         // Direct alarm listener callback timeout
         public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
 
+        public long[] APP_STANDBY_MIN_DELAYS = new long[DEFAULT_APP_STANDBY_DELAYS.length];
+
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
         private long mLastAllowWhileIdleWhitelistDuration = -1;
@@ -328,7 +362,12 @@
                         DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
                 LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
                         DEFAULT_LISTENER_TIMEOUT);
-
+                APP_STANDBY_MIN_DELAYS[0] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[0],
+                        DEFAULT_APP_STANDBY_DELAYS[0]);
+                for (int i = 1; i < KEYS_APP_STANDBY_DELAY.length; i++) {
+                    APP_STANDBY_MIN_DELAYS[i] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[i],
+                            Math.max(APP_STANDBY_MIN_DELAYS[i-1], DEFAULT_APP_STANDBY_DELAYS[i]));
+                }
                 updateAllowWhileIdleWhitelistDurationLocked();
             }
         }
@@ -359,6 +398,12 @@
             pw.print("    "); pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("=");
             TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
             pw.println();
+
+            for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) {
+                pw.print("    "); pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
+                TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw);
+                pw.println();
+            }
         }
 
         void dumpProto(ProtoOutputStream proto, long fieldId) {
@@ -618,9 +663,7 @@
             }
 
             PriorityClass packagePrio = a.priorityClass;
-            String alarmPackage = (a.operation != null)
-                    ? a.operation.getCreatorPackage()
-                    : a.packageName;
+            String alarmPackage = a.sourcePackage;
             if (packagePrio == null) packagePrio = mPriorities.get(alarmPackage);
             if (packagePrio == null) {
                 packagePrio = a.priorityClass = new PriorityClass(); // lowest prio & stale sequence
@@ -751,6 +794,7 @@
     }
 
     void rebatchAllAlarmsLocked(boolean doValidate) {
+        long start = mStatLogger.getTime();
         final int oldCount =
                 getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
         final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
@@ -790,6 +834,7 @@
 
         rescheduleKernelAlarmsLocked();
         updateNextAlarmClockLocked();
+        mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start);
     }
 
     void reAddAlarmLocked(Alarm a, long nowElapsed, boolean doValidate) {
@@ -905,7 +950,7 @@
             // Recurring alarms may have passed several alarm intervals while the
             // alarm was kept pending. Send the appropriate trigger count.
             if (alarm.repeatInterval > 0) {
-                alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
+                alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
                 final long nextElapsed = alarm.whenElapsed + delta;
@@ -1228,6 +1273,8 @@
             mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
+            mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+            mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
         }
     }
 
@@ -1377,6 +1424,51 @@
         setImplLocked(a, false, doValidate);
     }
 
+    private long getMinDelayForBucketLocked(int bucket) {
+        // Return the minimum time that should elapse before an app in the specified bucket
+        // can receive alarms again
+        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
+            return mConstants.APP_STANDBY_MIN_DELAYS[4];
+        }
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) {
+            return mConstants.APP_STANDBY_MIN_DELAYS[3];
+        }
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
+            return mConstants.APP_STANDBY_MIN_DELAYS[2];
+        }
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+            return mConstants.APP_STANDBY_MIN_DELAYS[1];
+        }
+        else return mConstants.APP_STANDBY_MIN_DELAYS[0];
+    }
+
+    private void adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
+        if (alarm.alarmClock != null || UserHandle.isCore(alarm.creatorUid)) {
+            return;
+        }
+        if (mAppStandbyParole) {
+            if (alarm.whenElapsed > alarm.requestedWhenElapsed) {
+                // We did throttle this alarm earlier, restore original requirements
+                alarm.whenElapsed = alarm.requestedWhenElapsed;
+                alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+            }
+            return;
+        }
+        final String sourcePackage = alarm.sourcePackage;
+        final int sourceUserId = UserHandle.getUserId(alarm.creatorUid);
+        final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
+                sourcePackage, sourceUserId, SystemClock.elapsedRealtime());
+
+        final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId);
+        final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
+        if (lastElapsed > 0) {
+            final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
+            if (alarm.requestedWhenElapsed < minElapsed) {
+                alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+            }
+        }
+    }
+
     private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
             // This is a special alarm that will put the system into idle until it goes off.
@@ -1428,6 +1520,7 @@
                 mAllowWhileIdleDispatches.add(ent);
             }
         }
+        adjustDeliveryTimeBasedOnStandbyBucketLocked(a);
 
         int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
                 ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
@@ -1655,6 +1748,9 @@
             mForceAppStandbyTracker.dump(pw, "  ");
             pw.println();
 
+            pw.println("  App Standby Parole: " + mAppStandbyParole);
+            pw.println();
+
             final long nowRTC = System.currentTimeMillis();
             final long nowELAPSED = SystemClock.elapsedRealtime();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
@@ -1753,6 +1849,15 @@
             }
             pw.println("]");
 
+            pw.println("  mLastAlarmDeliveredForPackage:");
+            for (int i = 0; i < mLastAlarmDeliveredForPackage.size(); i++) {
+                Pair<String, Integer> packageUser = mLastAlarmDeliveredForPackage.keyAt(i);
+                pw.print("    Package " + packageUser.first + ", User " + packageUser.second + ":");
+                TimeUtils.formatDuration(mLastAlarmDeliveredForPackage.valueAt(i), nowELAPSED, pw);
+                pw.println();
+            }
+            pw.println();
+
             if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
                 pw.println();
                 pw.println("    Idle mode state:");
@@ -1913,6 +2018,8 @@
                     }
                 }
             }
+            pw.println();
+            mStatLogger.dump(pw, "  ");
 
             if (RECORD_DEVICE_IDLE_ALARMS) {
                 pw.println();
@@ -2746,8 +2853,7 @@
             // Don't block starting foreground components
             return false;
         }
-        final String sourcePackage =
-                (alarm.operation != null) ? alarm.operation.getCreatorPackage() : alarm.packageName;
+        final String sourcePackage = alarm.sourcePackage;
         final int sourceUid = alarm.creatorUid;
         return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
                 allowWhileIdle);
@@ -2856,7 +2962,7 @@
                 if (alarm.repeatInterval > 0) {
                     // this adjustment will be zero if we're late by
                     // less than one full repeat interval
-                    alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
+                    alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
 
                     // Also schedule its next recurrence
                     final long delta = alarm.count * alarm.repeatInterval;
@@ -2925,11 +3031,14 @@
         public final int uid;
         public final int creatorUid;
         public final String packageName;
+        public final String sourcePackage;
         public int count;
         public long when;
         public long windowLength;
         public long whenElapsed;    // 'when' in the elapsed time base
         public long maxWhenElapsed; // also in the elapsed time base
+        public final long requestedWhenElapsed; // original expiry time requested by the app
+        public final long requestedMaxWhenElapsed;
         public long repeatInterval;
         public PriorityClass priorityClass;
 
@@ -2943,8 +3052,10 @@
                     || _type == AlarmManager.RTC_WAKEUP;
             when = _when;
             whenElapsed = _whenElapsed;
+            requestedWhenElapsed = _whenElapsed;
             windowLength = _windowLength;
             maxWhenElapsed = _maxWhen;
+            requestedMaxWhenElapsed = _maxWhen;
             repeatInterval = _interval;
             operation = _op;
             listener = _rec;
@@ -2955,7 +3066,7 @@
             alarmClock = _info;
             uid = _uid;
             packageName = _pkgName;
-
+            sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
             creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
         }
 
@@ -2980,9 +3091,7 @@
         }
 
         public boolean matches(String packageName) {
-            return (operation != null)
-                    ? packageName.equals(operation.getTargetPackage())
-                    : packageName.equals(this.packageName);
+            return packageName.equals(sourcePackage);
         }
 
         @Override
@@ -2995,11 +3104,7 @@
             sb.append(" when ");
             sb.append(when);
             sb.append(" ");
-            if (operation != null) {
-                sb.append(operation.getTargetPackage());
-            } else {
-                sb.append(packageName);
-            }
+            sb.append(sourcePackage);
             sb.append('}');
             return sb.toString();
         }
@@ -3009,6 +3114,8 @@
             final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
             pw.print(prefix); pw.print("tag="); pw.println(statsTag);
             pw.print(prefix); pw.print("type="); pw.print(type);
+                    pw.print(" requestedWhenELapsed="); TimeUtils.formatDuration(
+                            requestedWhenElapsed, nowELAPSED, pw);
                     pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
                             nowELAPSED, pw);
                     pw.print(" when=");
@@ -3249,8 +3356,6 @@
                             // alarms, we need to merge them in to the list.  note we don't
                             // just deliver them first because we generally want non-wakeup
                             // alarms delivered after wakeup alarms.
-                            rescheduleKernelAlarmsLocked();
-                            updateNextAlarmClockLocked();
                             if (mPendingNonWakeupAlarms.size() > 0) {
                                 calculateDeliveryPriorities(mPendingNonWakeupAlarms);
                                 triggerList.addAll(mPendingNonWakeupAlarms);
@@ -3262,6 +3367,27 @@
                                 }
                                 mPendingNonWakeupAlarms.clear();
                             }
+                            boolean needRebatch = false;
+                            final HashSet<String> triggerPackages = new HashSet<>();
+                            for (int i = triggerList.size() - 1; i >= 0; i--) {
+                                triggerPackages.add(triggerList.get(i).sourcePackage);
+                            }
+                            outer:
+                            for (int i = 0; i < mAlarmBatches.size(); i++) {
+                                final Batch batch = mAlarmBatches.get(i);
+                                for (int j = 0; j < batch.size(); j++) {
+                                    if (triggerPackages.contains(batch.get(j))) {
+                                        needRebatch = true;
+                                        break outer;
+                                    }
+                                }
+                            }
+                            if (needRebatch) {
+                                rebatchAllAlarmsLocked(false);
+                            } else {
+                                rescheduleKernelAlarmsLocked();
+                                updateNextAlarmClockLocked();
+                            }
                             deliverAlarmsLocked(triggerList, nowELAPSED);
                         }
                     }
@@ -3318,6 +3444,8 @@
         public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
         public static final int LISTENER_TIMEOUT = 3;
         public static final int REPORT_ALARMS_ACTIVE = 4;
+        public static final int APP_STANDBY_BUCKET_CHANGED = 5;
+        public static final int APP_STANDBY_PAROLE_CHANGED = 6;
 
         public AlarmHandler() {
         }
@@ -3363,6 +3491,19 @@
                     }
                     break;
 
+                case APP_STANDBY_PAROLE_CHANGED:
+                    synchronized (mLock) {
+                        mAppStandbyParole = (Boolean) msg.obj;
+                        rebatchAllAlarmsLocked(false);
+                    }
+                    break;
+
+                case APP_STANDBY_BUCKET_CHANGED:
+                    synchronized (mLock) {
+                        rebatchAllAlarmsLocked(false);
+                    }
+                    break;
+
                 default:
                     // nope, just ignore it
                     break;
@@ -3489,6 +3630,13 @@
                     int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                     if (userHandle >= 0) {
                         removeUserLocked(userHandle);
+                        for (int i = mLastAlarmDeliveredForPackage.size() - 1; i >= 0; i--) {
+                            final Pair<String, Integer> packageUser =
+                                    mLastAlarmDeliveredForPackage.keyAt(i);
+                            if (packageUser.second == userHandle) {
+                                mLastAlarmDeliveredForPackage.removeAt(i);
+                            }
+                        }
                     }
                 } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
                     if (uid >= 0) {
@@ -3509,6 +3657,13 @@
                     }
                 }
                 if (pkgList != null && (pkgList.length > 0)) {
+                    for (int i = mLastAlarmDeliveredForPackage.size() - 1; i >= 0; i--) {
+                        Pair<String, Integer> packageUser = mLastAlarmDeliveredForPackage.keyAt(i);
+                        if (ArrayUtils.contains(pkgList, packageUser.first)
+                                && packageUser.second == UserHandle.getUserId(uid)) {
+                            mLastAlarmDeliveredForPackage.removeAt(i);
+                        }
+                    }
                     for (String pkg : pkgList) {
                         if (uid >= 0) {
                             // package-removed case
@@ -3563,6 +3718,33 @@
         }
     };
 
+    /**
+     * Tracking of app assignments to standby buckets
+     */
+    final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
+        @Override
+        public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
+                boolean idle, int bucket) {
+            if (DEBUG_STANDBY) {
+                Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
+                        bucket);
+            }
+            mHandler.removeMessages(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
+            mHandler.sendEmptyMessage(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
+        }
+
+        @Override
+        public void onParoleStateChanged(boolean isParoleOn) {
+            if (DEBUG_STANDBY) {
+                Slog.d(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
+            }
+            mHandler.removeMessages(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
+            mHandler.removeMessages(AlarmHandler.APP_STANDBY_PAROLE_CHANGED);
+            mHandler.obtainMessage(AlarmHandler.APP_STANDBY_PAROLE_CHANGED,
+                    Boolean.valueOf(isParoleOn)).sendToTarget();
+        }
+    };
+
     private final Listener mForceAppStandbyListener = new Listener() {
         @Override
         public void unblockAllUnrestrictedAlarms() {
@@ -3841,7 +4023,6 @@
                     alarm.packageName, alarm.type, alarm.statsTag, nowELAPSED);
             mInFlight.add(inflight);
             mBroadcastRefCount++;
-
             if (allowWhileIdle) {
                 // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
                 mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
@@ -3860,6 +4041,11 @@
                     mAllowWhileIdleDispatches.add(ent);
                 }
             }
+            if (!UserHandle.isCore(alarm.creatorUid)) {
+                final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
+                        UserHandle.getUserId(alarm.creatorUid));
+                mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);
+            }
 
             final BroadcastStats bs = inflight.mBroadcastStats;
             bs.count++;
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a12c85a..44974ff 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -141,6 +141,8 @@
     private boolean mHasNetworkLocation;
     private Location mLastGenericLocation;
     private Location mLastGpsLocation;
+    // Current locked state of the screen
+    private boolean mScreenLocked;
 
     /** Device is currently active. */
     private static final int STATE_ACTIVE = 0;
@@ -156,6 +158,7 @@
     private static final int STATE_IDLE = 5;
     /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
     private static final int STATE_IDLE_MAINTENANCE = 6;
+
     private static String stateToString(int state) {
         switch (state) {
             case STATE_ACTIVE: return "ACTIVE";
@@ -547,6 +550,11 @@
                 "sms_temp_app_whitelist_duration";
         private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
                 "notification_whitelist_duration";
+        /**
+         * Whether to wait for the user to unlock the device before causing screen-on to
+         * exit doze. Default = true
+         */
+        private static final String KEY_WAIT_FOR_UNLOCK = "wait_for_unlock";
 
         /**
          * This is the time, after becoming inactive, that we go in to the first
@@ -765,6 +773,8 @@
          */
         public long NOTIFICATION_WHITELIST_DURATION;
 
+        public boolean WAIT_FOR_UNLOCK;
+
         private final ContentResolver mResolver;
         private final boolean mSmallBatteryDevice;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -855,6 +865,7 @@
                         KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
                 NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+                WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, false);
             }
         }
 
@@ -962,6 +973,9 @@
             pw.print("    "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
             TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
             pw.println();
+
+            pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
+            pw.println(WAIT_FOR_UNLOCK);
         }
     }
 
@@ -1340,6 +1354,19 @@
         }
     }
 
+    private ActivityManagerInternal.ScreenObserver mScreenObserver =
+            new ActivityManagerInternal.ScreenObserver() {
+                @Override
+                public void onAwakeStateChanged(boolean isAwake) { }
+
+                @Override
+                public void onKeyguardStateChanged(boolean isShowing) {
+                    synchronized (DeviceIdleController.this) {
+                        DeviceIdleController.this.keyguardShowingLocked(isShowing);
+                    }
+                }
+            };
+
     public DeviceIdleController(Context context) {
         super(context);
         mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
@@ -1406,6 +1433,7 @@
 
             mNetworkConnected = true;
             mScreenOn = true;
+            mScreenLocked = false;
             // Start out assuming we are charging.  If we aren't, we will at least get
             // a battery update the next time the level drops.
             mCharging = true;
@@ -1501,6 +1529,8 @@
                 mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
+                mLocalActivityManager.registerScreenObserver(mScreenObserver);
+
                 passWhiteListToForceAppStandbyTrackerLocked();
                 updateInteractivityLocked();
             }
@@ -1976,7 +2006,7 @@
             }
         } else if (screenOn) {
             mScreenOn = true;
-            if (!mForceIdle) {
+            if (!mForceIdle && (!mScreenLocked || !mConstants.WAIT_FOR_UNLOCK)) {
                 becomeActiveLocked("screen", Process.myUid());
             }
         }
@@ -1997,6 +2027,16 @@
         }
     }
 
+    void keyguardShowingLocked(boolean showing) {
+        if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
+        if (mScreenLocked != showing) {
+            mScreenLocked = showing;
+            if (mScreenOn && !mForceIdle && !mScreenLocked) {
+                becomeActiveLocked("unlocked", Process.myUid());
+            }
+        }
+    }
+
     void scheduleReportActiveLocked(String activeReason, int activeUid) {
         Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
         mHandler.sendMessage(msg);
@@ -3308,6 +3348,7 @@
             pw.print("  mForceIdle="); pw.println(mForceIdle);
             pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mScreenLocked="); pw.println(mScreenLocked);
             pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
             pw.print("  mMotionActive="); pw.println(mMotionListener.active);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 219facd..0502117 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,7 +34,7 @@
 2731 power_soft_sleep_requested (savedwaketimems|2)
 # Power save state has changed. See BatterySaverController.java for the details.
 2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
-27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
 
 #
 # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index fe4ac6d7..a07a982 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -87,6 +87,7 @@
     private static final String NETD_SERVICE_NAME = "netd";
     private static final int[] DIRECTIONS =
             new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
+    private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
 
     private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
     private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -413,12 +414,16 @@
                     .append(mTransformQuotaTracker)
                     .append(", mSocketQuotaTracker=")
                     .append(mSocketQuotaTracker)
+                    .append(", mTunnelQuotaTracker=")
+                    .append(mTunnelQuotaTracker)
                     .append(", mSpiRecords=")
                     .append(mSpiRecords)
                     .append(", mTransformRecords=")
                     .append(mTransformRecords)
                     .append(", mEncapSocketRecords=")
                     .append(mEncapSocketRecords)
+                    .append(", mTunnelInterfaceRecords=")
+                    .append(mTunnelInterfaceRecords)
                     .append("}")
                     .toString();
         }
@@ -815,12 +820,14 @@
             try {
                 mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
 
-                for (int direction : DIRECTIONS) {
-                    int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
-                    mSrvConfig
-                            .getNetdInstance()
-                            .ipSecDeleteSecurityPolicy(
-                                    0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff);
+                for(String wildcardAddr : WILDCARD_ADDRESSES) {
+                    for (int direction : DIRECTIONS) {
+                        int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+                        mSrvConfig
+                                .getNetdInstance()
+                                .ipSecDeleteSecurityPolicy(
+                                        0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
+                    }
                 }
             } catch (ServiceSpecificException e) {
                 // FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -1261,19 +1268,21 @@
                     .getNetdInstance()
                     .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
 
-            for (int direction : DIRECTIONS) {
-                int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+            for(String wildcardAddr : WILDCARD_ADDRESSES) {
+                for (int direction : DIRECTIONS) {
+                    int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
 
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecAddSecurityPolicy(
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecAddSecurityPolicy(
                                 0, // Use 0 for reqId
                                 direction,
-                                "",
-                                "",
+                                wildcardAddr,
+                                wildcardAddr,
                                 0,
                                 mark,
                                 0xffffffff);
+                }
             }
 
             userRecord.mTunnelInterfaceRecords.put(
@@ -1646,16 +1655,18 @@
                 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
 
                 // If outbound, also add SPI to the policy.
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecUpdateSecurityPolicy(
-                                0, // Use 0 for reqId
-                                direction,
-                                "",
-                                "",
-                                transformInfo.getSpiRecord().getSpi(),
-                                mark,
-                                0xffffffff);
+                for(String wildcardAddr : WILDCARD_ADDRESSES) {
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecUpdateSecurityPolicy(
+                                    0, // Use 0 for reqId
+                                    direction,
+                                    wildcardAddr,
+                                    wildcardAddr,
+                                    transformInfo.getSpiRecord().getSpi(),
+                                    mark,
+                                    0xffffffff);
+                }
             }
 
             // Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1dd92f3..65e90bad 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1397,23 +1397,6 @@
     }
 
     /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param uid      the requestor's UID
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLockedForUser(
-            String provider, int uid, int userId) {
-        if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
-            return false;
-        }
-        return isLocationProviderEnabledForUser(provider, userId);
-    }
-
-    /**
      * Returns the permission string associated with the specified resolution level.
      *
      * @param resolutionLevel the resolution level
@@ -2585,143 +2568,6 @@
     }
 
     /**
-     * Method for enabling or disabling location.
-     *
-     * @param enabled true to enable location. false to disable location
-     * @param userId the user id to set
-     */
-    @Override
-    public void setLocationEnabledForUser(boolean enabled, int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        // Enable or disable all location providers. Fused provider and passive provider are
-        // excluded.
-        synchronized (mLock) {
-            for(String provider : getAllProvidersForLocationSettings()) {
-                setProviderEnabledForUser(provider, enabled, userId);
-            }
-        }
-    }
-
-    /**
-     * Returns the current enabled/disabled status of location
-     *
-     * @param userId the user id to query
-     * @return true if location is enabled. false if location is disabled.
-     */
-    @Override
-    public boolean isLocationEnabledForUser(int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        // If at least one location provider is enabled, return true. Fused provider and passive
-        // provider are excluded.
-        synchronized (mLock) {
-            for (String provider : getAllProvidersForLocationSettings()) {
-                if (isProviderEnabledForUser(provider, userId)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    @Override
-    public boolean isProviderEnabled(String provider) {
-        return isProviderEnabledForUser(provider, UserHandle.getCallingUserId());
-    }
-
-    /**
-     * Method for determining if a location provider is enabled.
-     *
-     * @param provider the location provider to query
-     * @param userId the user id to query
-     * @return true if the provider is enabled
-     */
-    @Override
-    public boolean isProviderEnabledForUser(String provider, int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
-        // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
-        int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                LocationProviderInterface p = mProvidersByName.get(provider);
-                return p != null
-                    && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Method for enabling or disabling a single location provider.
-     *
-     * @param provider the name of the provider
-     * @param enabled true to enable the provider. false to disable the provider
-     * @param userId the user id to set
-     * @return true if the value was set successfully. false on failure.
-     */
-    @Override
-    public boolean setProviderEnabledForUser(
-            String provider, boolean enabled, int userId) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS,
-                "Requires WRITE_SECURE_SETTINGS permission");
-
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                if (enabled) {
-                    provider = "+" + provider;
-                } else {
-                    provider = "-" + provider;
-                }
-                return Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        provider,
-                        userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Return all location providers except fused provider and passive provider. These two
-     * providers are not generating location by themselves, but only echo locations from other
-     * providers.
-     *
-     * @return All location providers except fused provider and passive provider, including
-     *          providers that are not permitted to be accessed by the calling activity or are
-     *          currently disabled.
-     */
-    private List<String> getAllProvidersForLocationSettings() {
-        List<String> providersForSettings = new ArrayList<>(mProviders.size());
-        for (String provider : getAllProviders()) {
-            if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
-                continue;
-            }
-            providersForSettings.add(provider);
-        }
-        return providersForSettings;
-    }
-
-    /**
      * Read location provider status from Settings.Secure
      *
      * @param provider the location provider to query
@@ -2742,23 +2588,6 @@
     }
 
     /**
-     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
-     * current user id
-     *
-     * @param userId the user id to get or set value
-     */
-    private void checkInteractAcrossUsersPermission(int userId) {
-        int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
-            if (ActivityManager.checkComponentPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
-                != PERMISSION_GRANTED) {
-                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
-            }
-        }
-    }
-
-    /**
      * Returns "true" if the UID belongs to a bound location provider.
      *
      * @param uid the uid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b7e5d37..f370393 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4027,9 +4027,13 @@
                 runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
             }
             String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
-            if ("true".equals(genDebugInfoProperty)) {
+            if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
                 runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
             }
+            String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+            if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+                runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+            }
             if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
                 runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
             }
@@ -4331,7 +4335,9 @@
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
             component.userId, component.realActivity.getPackageName(),
-            component.realActivity.getShortClassName(), resumed ? 1 : 0);
+            component.realActivity.getShortClassName(), resumed ?
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
         if (resumed) {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -12844,7 +12850,8 @@
         }
 
         // TODO: Where should the corresponding '1' (start) write go?
-        StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+        StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
+                StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
 
         boolean timedout = false;
 
@@ -25424,6 +25431,7 @@
         public void notifyAppTransitionFinished() {
             synchronized (ActivityManagerService.this) {
                 mStackSupervisor.notifyAppTransitionDone();
+                mKeyguardController.notifyAppTransitionDone();
             }
         }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0d96468..ea52782 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -325,39 +325,38 @@
     void noteProcessStart(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessStartLocked(name, uid);
-            // TODO: decide where this should be and use a constant instead of a literal.
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
+            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
         }
     }
 
     void noteProcessCrash(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessCrashLocked(name, uid);
-            // TODO: decide where this should be and use a constant instead of a literal.
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
+            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
         }
     }
 
     void noteProcessAnr(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessAnrLocked(name, uid);
-            // TODO: decide where this should be and use a constant instead of a literal.
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
+            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
         }
     }
 
     void noteProcessFinish(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessFinishLocked(name, uid);
-            // TODO: decide where this should be and use a constant instead of a literal.
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
+            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
         }
     }
 
     /** @param state Process state from ActivityManager.java. */
     void noteUidProcessState(int uid, int state) {
         synchronized (mStats) {
-            // TODO: remove this once we figure out properly where and how
             StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
                     ActivityManager.processStateAmToProto(state));
 
@@ -603,7 +602,6 @@
         enforceCallingPermission();
         if (DBG) Slog.d(TAG, "begin noteScreenState");
         synchronized (mStats) {
-            // TODO: remove this once we figure out properly where and how
             StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
 
             mStats.noteScreenStateLocked(state);
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 79f3fe3..05305f3 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -384,4 +384,8 @@
         proto.write(KEYGUARD_OCCLUDED, mOccluded);
         proto.end(token);
     }
+
+    public void notifyAppTransitionDone() {
+        setKeyguardGoingAway(false);
+    }
 }
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 21f9135..e5762d2 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -38,6 +38,7 @@
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -142,14 +143,6 @@
     TelecomManager mTelecomManager;
 
     /**
-     * Helper that is responsible for showing the right toast when a disallowed activity operation
-     * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
-     * fully locked mode we only show that unlocking is blocked.
-     */
-    @VisibleForTesting
-    LockTaskNotify mLockTaskNotify;
-
-    /**
      * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
      *
      * The first task in the list, which started the current LockTask session, is called the root
@@ -475,7 +468,7 @@
                 getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
             }
             if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                getLockTaskNotify().showPinningExitToast();
+                getStatusBarService().showPinningEnterExitToast(false /* entering */);
             }
         } catch (RemoteException ex) {
             throw new RuntimeException(ex);
@@ -490,7 +483,11 @@
      */
     void showLockTaskToast() {
         if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-            mHandler.post(() -> getLockTaskNotify().showEscapeToast());
+            try {
+                getStatusBarService().showPinningEscapeToast();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to send pinning escape toast", e);
+            }
         }
     }
 
@@ -582,7 +579,7 @@
         // When lock task starts, we disable the status bars.
         try {
             if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                getLockTaskNotify().showPinningStartToast();
+                getStatusBarService().showPinningEnterExitToast(true /* entering */);
             }
             mLockTaskModeState = lockTaskModeState;
             setStatusBarState(lockTaskModeState, userId);
@@ -835,15 +832,6 @@
         return mTelecomManager;
     }
 
-    // Should only be called on the handler thread
-    @NonNull
-    private LockTaskNotify getLockTaskNotify() {
-        if (mLockTaskNotify == null) {
-            mLockTaskNotify = new LockTaskNotify(mContext);
-        }
-        return mLockTaskNotify;
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "LockTaskController");
         prefix = prefix + "  ";
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f247de7..1825db8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2292,7 +2292,9 @@
         // only mute for the current user
         if (getCurrentUserId() == userId) {
             final boolean currentMute = AudioSystem.isMicrophoneMuted();
+            final long identity = Binder.clearCallingIdentity();
             AudioSystem.muteMicrophone(on);
+            Binder.restoreCallingIdentity(identity);
             if (on != currentMute) {
                 mContext.sendBroadcast(new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
                         .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
@@ -2666,7 +2668,9 @@
             }
 
             if (actualMode != mMode) {
+                final long identity = Binder.clearCallingIdentity();
                 status = AudioSystem.setPhoneState(actualMode);
+                Binder.restoreCallingIdentity(identity);
                 if (status == AudioSystem.AUDIO_STATUS_OK) {
                     if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
                     mMode = actualMode;
@@ -7275,11 +7279,15 @@
             if (mHasFocusListener) {
                 mMediaFocusControl.removeFocusFollower(mPolicyCallback);
             }
+            final long identity = Binder.clearCallingIdentity();
             AudioSystem.registerPolicyMixes(mMixes, false);
+            Binder.restoreCallingIdentity(identity);
         }
 
         void connectMixes() {
+            final long identity = Binder.clearCallingIdentity();
             AudioSystem.registerPolicyMixes(mMixes, true);
+            Binder.restoreCallingIdentity(identity);
         }
     };
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index be6c4a1..9a9cdbd 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1095,7 +1095,8 @@
         if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
         UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
         synchronized (mPublicSync) {
-            usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
+            usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
+                    : UsbManager.FUNCTION_NONE);
         }
         return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
new file mode 100644
index 0000000..6e571bd
--- /dev/null
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class that stores stats of ambient brightness regions as histogram.
+ */
+public class AmbientBrightnessStatsTracker {
+
+    private static final String TAG = "AmbientBrightnessStatsTracker";
+    private static final boolean DEBUG = false;
+
+    @VisibleForTesting
+    static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS =
+            {0, 0.1f, 0.3f, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000};
+    @VisibleForTesting
+    static final int MAX_DAYS_TO_TRACK = 7;
+
+    private final AmbientBrightnessStats mAmbientBrightnessStats;
+    private final Timer mTimer;
+    private final Injector mInjector;
+    private final UserManager mUserManager;
+    private float mCurrentAmbientBrightness;
+    private @UserIdInt int mCurrentUserId;
+
+    public AmbientBrightnessStatsTracker(UserManager userManager, @Nullable Injector injector) {
+        mUserManager = userManager;
+        if (injector != null) {
+            mInjector = injector;
+        } else {
+            mInjector = new Injector();
+        }
+        mAmbientBrightnessStats = new AmbientBrightnessStats();
+        mTimer = new Timer(() -> mInjector.elapsedRealtimeMillis());
+        mCurrentAmbientBrightness = -1;
+    }
+
+    public synchronized void start() {
+        mTimer.reset();
+        mTimer.start();
+    }
+
+    public synchronized void stop() {
+        if (mTimer.isRunning()) {
+            mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+                    mCurrentAmbientBrightness, mTimer.totalDurationSec());
+        }
+        mTimer.reset();
+        mCurrentAmbientBrightness = -1;
+    }
+
+    public synchronized void add(@UserIdInt int userId, float newAmbientBrightness) {
+        if (mTimer.isRunning()) {
+            if (userId == mCurrentUserId) {
+                mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+                        mCurrentAmbientBrightness, mTimer.totalDurationSec());
+            } else {
+                if (DEBUG) {
+                    Slog.v(TAG, "User switched since last sensor event.");
+                }
+                mCurrentUserId = userId;
+            }
+            mTimer.reset();
+            mTimer.start();
+            mCurrentAmbientBrightness = newAmbientBrightness;
+        } else {
+            if (DEBUG) {
+                Slog.e(TAG, "Timer not running while trying to add brightness stats.");
+            }
+        }
+    }
+
+    public synchronized void writeStats(OutputStream stream) throws IOException {
+        mAmbientBrightnessStats.writeToXML(stream);
+    }
+
+    public synchronized void readStats(InputStream stream) throws IOException {
+        mAmbientBrightnessStats.readFromXML(stream);
+    }
+
+    public synchronized ArrayList<AmbientBrightnessDayStats> getUserStats(int userId) {
+        return mAmbientBrightnessStats.getUserStats(userId);
+    }
+
+    public synchronized void dump(PrintWriter pw) {
+        pw.println("AmbientBrightnessStats:");
+        pw.print(mAmbientBrightnessStats);
+    }
+
+    /**
+     * AmbientBrightnessStats tracks ambient brightness stats across users over multiple days.
+     * This class is not ThreadSafe.
+     */
+    class AmbientBrightnessStats {
+
+        private static final String TAG_AMBIENT_BRIGHTNESS_STATS = "ambient-brightness-stats";
+        private static final String TAG_AMBIENT_BRIGHTNESS_DAY_STATS =
+                "ambient-brightness-day-stats";
+        private static final String ATTR_USER = "user";
+        private static final String ATTR_LOCAL_DATE = "local-date";
+        private static final String ATTR_BUCKET_BOUNDARIES = "bucket-boundaries";
+        private static final String ATTR_BUCKET_STATS = "bucket-stats";
+
+        private Map<Integer, Deque<AmbientBrightnessDayStats>> mStats;
+
+        public AmbientBrightnessStats() {
+            mStats = new HashMap<>();
+        }
+
+        public void log(@UserIdInt int userId, LocalDate localDate, float ambientBrightness,
+                float durationSec) {
+            Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(mStats, userId);
+            AmbientBrightnessDayStats dayStats = getOrCreateDayStats(userStats, localDate);
+            dayStats.log(ambientBrightness, durationSec);
+        }
+
+        public ArrayList<AmbientBrightnessDayStats> getUserStats(@UserIdInt int userId) {
+            if (mStats.containsKey(userId)) {
+                return new ArrayList<>(mStats.get(userId));
+            } else {
+                return null;
+            }
+        }
+
+        public void writeToXML(OutputStream stream) throws IOException {
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+            out.startTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+            for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+                for (AmbientBrightnessDayStats userDayStats : entry.getValue()) {
+                    int userSerialNumber = mInjector.getUserSerialNumber(mUserManager,
+                            entry.getKey());
+                    if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
+                        out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+                        out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
+                        out.attribute(null, ATTR_LOCAL_DATE,
+                                userDayStats.getLocalDate().toString());
+                        StringBuilder bucketBoundariesValues = new StringBuilder();
+                        StringBuilder timeSpentValues = new StringBuilder();
+                        for (int i = 0; i < userDayStats.getBucketBoundaries().length; i++) {
+                            if (i > 0) {
+                                bucketBoundariesValues.append(",");
+                                timeSpentValues.append(",");
+                            }
+                            bucketBoundariesValues.append(userDayStats.getBucketBoundaries()[i]);
+                            timeSpentValues.append(userDayStats.getStats()[i]);
+                        }
+                        out.attribute(null, ATTR_BUCKET_BOUNDARIES,
+                                bucketBoundariesValues.toString());
+                        out.attribute(null, ATTR_BUCKET_STATS, timeSpentValues.toString());
+                        out.endTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+                    }
+                }
+            }
+            out.endTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+            out.endDocument();
+            stream.flush();
+        }
+
+        public void readFromXML(InputStream stream) throws IOException {
+            try {
+                Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>();
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                }
+                String tag = parser.getName();
+                if (!TAG_AMBIENT_BRIGHTNESS_STATS.equals(tag)) {
+                    throw new XmlPullParserException(
+                            "Ambient brightness stats not found in tracker file " + tag);
+                }
+
+                final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+                parser.next();
+                int outerDepth = parser.getDepth();
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+                    tag = parser.getName();
+                    if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
+                        String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
+                        LocalDate localDate = LocalDate.parse(
+                                parser.getAttributeValue(null, ATTR_LOCAL_DATE));
+                        String[] bucketBoundaries = parser.getAttributeValue(null,
+                                ATTR_BUCKET_BOUNDARIES).split(",");
+                        String[] bucketStats = parser.getAttributeValue(null,
+                                ATTR_BUCKET_STATS).split(",");
+                        if (bucketBoundaries.length != bucketStats.length
+                                || bucketBoundaries.length < 1) {
+                            throw new IOException("Invalid brightness stats string.");
+                        }
+                        float[] parsedBucketBoundaries = new float[bucketBoundaries.length];
+                        float[] parsedBucketStats = new float[bucketStats.length];
+                        for (int i = 0; i < bucketBoundaries.length; i++) {
+                            parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
+                            parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
+                        }
+                        int userId = mInjector.getUserId(mUserManager,
+                                Integer.parseInt(userSerialNumber));
+                        if (userId != -1 && localDate.isAfter(cutOffDate)) {
+                            Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
+                                    parsedStats, userId);
+                            userStats.offer(
+                                    new AmbientBrightnessDayStats(localDate,
+                                            parsedBucketBoundaries, parsedBucketStats));
+                        }
+                    }
+                }
+                mStats = parsedStats;
+            } catch (NullPointerException | NumberFormatException | XmlPullParserException |
+                    DateTimeParseException | IOException e) {
+                throw new IOException("Failed to parse brightness stats file.", e);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+                for (AmbientBrightnessDayStats dayStats : entry.getValue()) {
+                    builder.append("  ");
+                    builder.append(entry.getKey()).append(" ");
+                    builder.append(dayStats).append("\n");
+                }
+            }
+            return builder.toString();
+        }
+
+        private Deque<AmbientBrightnessDayStats> getOrCreateUserStats(
+                Map<Integer, Deque<AmbientBrightnessDayStats>> stats, @UserIdInt int userId) {
+            if (!stats.containsKey(userId)) {
+                stats.put(userId, new ArrayDeque<>());
+            }
+            return stats.get(userId);
+        }
+
+        private AmbientBrightnessDayStats getOrCreateDayStats(
+                Deque<AmbientBrightnessDayStats> userStats, LocalDate localDate) {
+            AmbientBrightnessDayStats lastBrightnessStats = userStats.peekLast();
+            if (lastBrightnessStats != null && lastBrightnessStats.getLocalDate().equals(
+                    localDate)) {
+                return lastBrightnessStats;
+            } else {
+                AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate,
+                        BUCKET_BOUNDARIES_FOR_NEW_STATS);
+                if (userStats.size() == MAX_DAYS_TO_TRACK) {
+                    userStats.poll();
+                }
+                userStats.offer(dayStats);
+                return dayStats;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    interface Clock {
+        long elapsedTimeMillis();
+    }
+
+    @VisibleForTesting
+    static class Timer {
+
+        private final Clock clock;
+        private long startTimeMillis;
+        private boolean started;
+
+        public Timer(Clock clock) {
+            this.clock = clock;
+        }
+
+        public void reset() {
+            started = false;
+        }
+
+        public void start() {
+            if (!started) {
+                startTimeMillis = clock.elapsedTimeMillis();
+                started = true;
+            }
+        }
+
+        public boolean isRunning() {
+            return started;
+        }
+
+        public float totalDurationSec() {
+            if (started) {
+                return (float) ((clock.elapsedTimeMillis() - startTimeMillis) / 1000.0);
+            }
+            return 0;
+        }
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        public long elapsedRealtimeMillis() {
+            return SystemClock.elapsedRealtime();
+        }
+
+        public int getUserSerialNumber(UserManager userManager, int userId) {
+            return userManager.getUserSerialNumber(userId);
+        }
+
+        public int getUserId(UserManager userManager, int userSerialNumber) {
+            return userManager.getUserHandle(userSerialNumber);
+        }
+
+        public LocalDate getLocalDate() {
+            return LocalDate.now();
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6a88b1e..e2aee88 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -269,6 +269,14 @@
         }
     }
 
+    public boolean hasUserDataPoints() {
+        return mBrightnessMapper.hasUserDataPoints();
+    }
+
+    public boolean isDefaultConfig() {
+        return mBrightnessMapper.isDefaultConfig();
+    }
+
     private boolean setDisplayPolicy(int policy) {
         if (mDisplayPolicy == policy) {
             return false;
diff --git a/services/core/java/com/android/server/display/BrightnessIdleJob.java b/services/core/java/com/android/server/display/BrightnessIdleJob.java
index 876acf4..b0a41cb 100644
--- a/services/core/java/com/android/server/display/BrightnessIdleJob.java
+++ b/services/core/java/com/android/server/display/BrightnessIdleJob.java
@@ -70,7 +70,7 @@
             Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events");
         }
         DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class);
-        dmi.persistBrightnessSliderEvents();
+        dmi.persistBrightnessTrackerState();
         return false;
     }
 
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 001d4d9..c0d2599 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -200,6 +200,12 @@
      */
     public abstract void clearUserDataPoints();
 
+    /** @return true if there are any short term adjustments applied to the curve */
+    public abstract boolean hasUserDataPoints();
+
+    /** @return true if the current brightness config is the default one */
+    public abstract boolean isDefaultConfig();
+
     public abstract void dump(PrintWriter pw);
 
     private static float normalizeAbsoluteBrightness(int brightness) {
@@ -390,6 +396,16 @@
         }
 
         @Override
+        public boolean hasUserDataPoints() {
+            return mUserLux != -1;
+        }
+
+        @Override
+        public boolean isDefaultConfig() {
+            return true;
+        }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("SimpleMappingStrategy");
             pw.println("  mSpline=" + mSpline);
@@ -507,6 +523,16 @@
         }
 
         @Override
+        public boolean hasUserDataPoints() {
+            return mUserLux != -1;
+        }
+
+        @Override
+        public boolean isDefaultConfig() {
+            return mDefaultConfig.equals(mConfig);
+        }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index bcf8bfe..6b4666a 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -28,8 +29,8 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
-import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Environment;
 import android.os.Handler;
@@ -81,6 +82,7 @@
     static final boolean DEBUG = false;
 
     private static final String EVENTS_FILE = "brightness_events.xml";
+    private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
     private static final int MAX_EVENTS = 100;
     // Discard events when reading or writing that are older than this.
     private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
@@ -99,6 +101,9 @@
     private static final String ATTR_NIGHT_MODE = "nightMode";
     private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
     private static final String ATTR_LAST_NITS = "lastNits";
+    private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
+    private static final String ATTR_POWER_SAVE = "powerSaveFactor";
+    private static final String ATTR_USER_POINT = "userPoint";
 
     private static final int MSG_BACKGROUND_START = 0;
     private static final int MSG_BRIGHTNESS_CHANGED = 1;
@@ -113,6 +118,10 @@
     private final Runnable mEventsWriter = () -> writeEvents();
     private volatile boolean mWriteEventsScheduled;
 
+    private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
+    private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats();
+    private volatile boolean mWriteBrightnessStatsScheduled;
+
     private UserManager mUserManager;
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -120,6 +129,7 @@
     // mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
+    private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
 
     // Lock held while collecting data related to brightness changes.
     private final Object mDataCollectionLock = new Object();
@@ -157,12 +167,19 @@
         }
         mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
         mUserManager = mContext.getSystemService(UserManager.class);
-
+        try {
+            final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
+            mCurrentUserId = focusedStack.userId;
+        } catch (RemoteException e) {
+            // Really shouldn't be possible.
+            return;
+        }
         mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
     }
 
     private void backgroundStart(float initialBrightness) {
         readEvents();
+        readAmbientBrightnessStats();
 
         mSensorListener = new SensorListener();
 
@@ -196,12 +213,20 @@
         mInjector.unregisterSensorListener(mContext, mSensorListener);
         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
         mInjector.cancelIdleJob(mContext);
+        mAmbientBrightnessStatsTracker.stop();
 
         synchronized (mDataCollectionLock) {
             mStarted = false;
         }
     }
 
+    public void onSwitchUser(@UserIdInt int newUserId) {
+        if (DEBUG) {
+            Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
+        }
+        mCurrentUserId = newUserId;
+    }
+
     /**
      * @param userId userId to fetch data for.
      * @param includePackage if false we will null out BrightnessChangeEvent.packageName
@@ -228,24 +253,29 @@
         return new ParceledListSlice<>(out);
     }
 
-    public void persistEvents() {
-        scheduleWriteEvents();
+    public void persistBrightnessTrackerState() {
+        scheduleWriteBrightnessTrackerState();
     }
 
     /**
      * Notify the BrightnessTracker that the user has changed the brightness of the display.
      */
-    public void notifyBrightnessChanged(float brightness, boolean userInitiated) {
+    public void notifyBrightnessChanged(float brightness, boolean userInitiated,
+            float powerBrightnessFactor, boolean isUserSetBrightness,
+            boolean isDefaultBrightnessConfig) {
         if (DEBUG) {
             Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
                         brightness, userInitiated));
         }
         Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
-                userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness);
+                userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
+                        powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig));
         m.sendToTarget();
     }
 
-    private void handleBrightnessChanged(float brightness, boolean userInitiated) {
+    private void handleBrightnessChanged(float brightness, boolean userInitiated,
+            float powerBrightnessFactor, boolean isUserSetBrightness,
+            boolean isDefaultBrightnessConfig) {
         BrightnessChangeEvent.Builder builder;
 
         synchronized (mDataCollectionLock) {
@@ -267,6 +297,9 @@
             builder = new BrightnessChangeEvent.Builder();
             builder.setBrightness(brightness);
             builder.setTimeStamp(mInjector.currentTimeMillis());
+            builder.setPowerBrightnessFactor(powerBrightnessFactor);
+            builder.setUserBrightnessPoint(isUserSetBrightness);
+            builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
 
             final int readingCount = mLastSensorReadings.size();
             if (readingCount == 0) {
@@ -321,11 +354,15 @@
         }
     }
 
-    private void scheduleWriteEvents() {
+    private void scheduleWriteBrightnessTrackerState() {
         if (!mWriteEventsScheduled) {
             mBgHandler.post(mEventsWriter);
             mWriteEventsScheduled = true;
         }
+        if (!mWriteBrightnessStatsScheduled) {
+            mBgHandler.post(mAmbientBrightnessStatsWriter);
+            mWriteBrightnessStatsScheduled = true;
+        }
     }
 
     private void writeEvents() {
@@ -336,7 +373,7 @@
                 return;
             }
 
-            final AtomicFile writeTo = mInjector.getFile();
+            final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
             if (writeTo == null) {
                 return;
             }
@@ -360,12 +397,29 @@
         }
     }
 
+    private void writeAmbientBrightnessStats() {
+        mWriteBrightnessStatsScheduled = false;
+        final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+        if (writeTo == null) {
+            return;
+        }
+        FileOutputStream output = null;
+        try {
+            output = writeTo.startWrite();
+            mAmbientBrightnessStatsTracker.writeStats(output);
+            writeTo.finishWrite(output);
+        } catch (IOException e) {
+            writeTo.failWrite(output);
+            Slog.e(TAG, "Failed to write ambient brightness stats.", e);
+        }
+    }
+
     private void readEvents() {
         synchronized (mEventsLock) {
             // Read might prune events so mark as dirty.
             mEventsDirty = true;
             mEvents.clear();
-            final AtomicFile readFrom = mInjector.getFile();
+            final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
             if (readFrom != null && readFrom.exists()) {
                 FileInputStream input = null;
                 try {
@@ -381,6 +435,23 @@
         }
     }
 
+    private void readAmbientBrightnessStats() {
+        mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
+        final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+        if (readFrom != null && readFrom.exists()) {
+            FileInputStream input = null;
+            try {
+                input = readFrom.openRead();
+                mAmbientBrightnessStatsTracker.readStats(input);
+            } catch (IOException e) {
+                readFrom.delete();
+                Slog.e(TAG, "Failed to read ambient brightness stats.", e);
+            } finally {
+                IoUtils.closeQuietly(input);
+            }
+        }
+    }
+
     @VisibleForTesting
     @GuardedBy("mEventsLock")
     void writeEventsLocked(OutputStream stream) throws IOException {
@@ -412,6 +483,12 @@
                         toWrite[i].colorTemperature));
                 out.attribute(null, ATTR_LAST_NITS,
                         Float.toString(toWrite[i].lastBrightness));
+                out.attribute(null, ATTR_DEFAULT_CONFIG,
+                        Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
+                out.attribute(null, ATTR_POWER_SAVE,
+                        Float.toString(toWrite[i].powerBrightnessFactor));
+                out.attribute(null, ATTR_USER_POINT,
+                        Boolean.toString(toWrite[i].isUserSetBrightness));
                 StringBuilder luxValues = new StringBuilder();
                 StringBuilder luxTimestamps = new StringBuilder();
                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -496,6 +573,21 @@
                     builder.setLuxValues(luxValues);
                     builder.setLuxTimestamps(luxTimestamps);
 
+                    String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
+                    if (defaultConfig != null) {
+                        builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
+                    }
+                    String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
+                    if (powerSave != null) {
+                        builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
+                    } else {
+                        builder.setPowerBrightnessFactor(1.0f);
+                    }
+                    String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
+                    if (userPoint != null) {
+                        builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
+                    }
+
                     BrightnessChangeEvent event = builder.build();
                     if (DEBUG) {
                         Slog.i(TAG, "Read event " + event.brightness
@@ -535,7 +627,11 @@
             BrightnessChangeEvent[] events = mEvents.toArray();
             for (int i = 0; i < events.length; ++i) {
                 pw.print("    " + events[i].timeStamp + ", " + events[i].userId);
-                pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness + ", {");
+                pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
+                pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
+                pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
+                pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
+                pw.print(" {");
                 for (int j = 0; j < events[i].luxValues.length; ++j){
                     if (j != 0) {
                         pw.print(", ");
@@ -545,6 +641,13 @@
                 pw.println("}");
             }
         }
+        if (mAmbientBrightnessStatsTracker != null) {
+            mAmbientBrightnessStatsTracker.dump(pw);
+        }
+    }
+
+    public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
+        return new ParceledListSlice<>(mAmbientBrightnessStatsTracker.getUserStats(userId));
     }
 
     // Not allowed to keep the SensorEvent so used to copy the data we care about.
@@ -584,6 +687,10 @@
         }
     }
 
+    private void recordAmbientBrightnessStats(SensorEvent event) {
+        mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
+    }
+
     private void batteryLevelChanged(int level, int scale) {
         synchronized (mDataCollectionLock) {
             mLastBatteryLevel = (float) level / (float) scale;
@@ -594,6 +701,7 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             recordSensorEvent(event);
+            recordAmbientBrightnessStats(event);
         }
 
         @Override
@@ -611,7 +719,7 @@
             String action = intent.getAction();
             if (Intent.ACTION_SHUTDOWN.equals(action)) {
                 stop();
-                scheduleWriteEvents();
+                scheduleWriteBrightnessTrackerState();
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
                 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
                 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
@@ -619,8 +727,10 @@
                     batteryLevelChanged(level, scale);
                 }
             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+                mAmbientBrightnessStatsTracker.stop();
                 mInjector.unregisterSensorListener(mContext, mSensorListener);
             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+                mAmbientBrightnessStatsTracker.start();
                 mInjector.registerSensorListener(mContext, mSensorListener,
                         mInjector.getBackgroundHandler());
             }
@@ -637,14 +747,31 @@
                     backgroundStart((float)msg.obj /*initial brightness*/);
                     break;
                 case MSG_BRIGHTNESS_CHANGED:
-                    float newBrightness = (float) msg.obj;
+                    BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
                     boolean userInitiatedChange = (msg.arg1 == 1);
-                    handleBrightnessChanged(newBrightness, userInitiatedChange);
+                    handleBrightnessChanged(values.brightness, userInitiatedChange,
+                            values.powerBrightnessFactor, values.isUserSetBrightness,
+                            values.isDefaultBrightnessConfig);
                     break;
             }
         }
     }
 
+    private static class BrightnessChangeValues {
+        final float brightness;
+        final float powerBrightnessFactor;
+        final boolean isUserSetBrightness;
+        final boolean isDefaultBrightnessConfig;
+
+        BrightnessChangeValues(float brightness, float powerBrightnessFactor,
+                boolean isUserSetBrightness, boolean isDefaultBrightnessConfig) {
+            this.brightness = brightness;
+            this.powerBrightnessFactor = powerBrightnessFactor;
+            this.isUserSetBrightness = isUserSetBrightness;
+            this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+        }
+    }
+
     @VisibleForTesting
     static class Injector {
         public void registerSensorListener(Context context,
@@ -679,8 +806,8 @@
             return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
         }
 
-        public AtomicFile getFile() {
-            return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
+        public AtomicFile getFile(String filename) {
+            return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
         }
 
         public long currentTimeMillis() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0c2ff05..a5c1fe2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -38,6 +38,7 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManagerGlobal;
@@ -359,6 +360,7 @@
                         mPersistentDataStore.getBrightnessConfiguration(userSerial);
                 mDisplayPowerController.setBrightnessConfiguration(config);
             }
+            mDisplayPowerController.onSwitchUser(newUserId);
         }
     }
 
@@ -1835,6 +1837,23 @@
         }
 
         @Override // Binder call
+        public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS,
+                    "Permission required to to access ambient light stats.");
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mSyncRoot) {
+                    return mDisplayPowerController.getAmbientBrightnessStats(userId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setBrightnessConfigurationForUser(
                 BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
             mContext.enforceCallingOrSelfPermission(
@@ -2039,9 +2058,9 @@
         }
 
         @Override
-        public void persistBrightnessSliderEvents() {
+        public void persistBrightnessTrackerState() {
             synchronized (mSyncRoot) {
-                mDisplayPowerController.persistBrightnessSliderEvents();
+                mDisplayPowerController.persistBrightnessTrackerState();
             }
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 80aec42..b27f1ec 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
@@ -478,6 +479,7 @@
         mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         mTemporaryScreenBrightness = -1;
+        mPendingScreenBrightnessSetting = -1;
         mTemporaryAutoBrightnessAdjustment = Float.NaN;
     }
 
@@ -498,11 +500,20 @@
         return mBrightnessTracker.getEvents(userId, includePackage);
     }
 
+    public void onSwitchUser(@UserIdInt int newUserId) {
+        mBrightnessTracker.onSwitchUser(newUserId);
+    }
+
+    public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
+            @UserIdInt int userId) {
+        return mBrightnessTracker.getAmbientBrightnessStats(userId);
+    }
+
     /**
-     * Persist the brightness slider events to disk.
+     * Persist the brightness slider events and ambient brightness stats to disk.
      */
-    public void persistBrightnessSliderEvents() {
-        mBrightnessTracker.persistEvents();
+    public void persistBrightnessTrackerState() {
+        mBrightnessTracker.persistBrightnessTrackerState();
     }
 
     /**
@@ -795,13 +806,15 @@
             brightness = PowerManager.BRIGHTNESS_ON;
         }
 
-        // If the brightness is already set then it's been overriden by something other than the
+        // If the brightness is already set then it's been overridden by something other than the
         // user, or is a temporary adjustment.
         final boolean userInitiatedChange = brightness < 0
                 && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
 
+        boolean hadUserBrightnessPoint = false;
         // Configure auto-brightness.
         if (mAutomaticBrightnessController != null) {
+            hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
             mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                     mBrightnessConfiguration,
                     mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
@@ -930,7 +943,7 @@
             }
 
             if (!brightnessIsTemporary) {
-                notifyBrightnessChanged(brightness, userInitiatedChange);
+                notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint);
             }
 
         }
@@ -1464,18 +1477,26 @@
             mPendingScreenBrightnessSetting = -1;
             return false;
         }
+        mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
         mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
         mPendingScreenBrightnessSetting = -1;
         return true;
     }
 
-    private void notifyBrightnessChanged(int brightness, boolean userInitiated) {
+    private void notifyBrightnessChanged(int brightness, boolean userInitiated,
+            boolean hadUserDataPoint) {
         final float brightnessInNits = convertToNits(brightness);
-        if (brightnessInNits >= 0.0f) {
+        if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+                && mAutomaticBrightnessController != null) {
             // We only want to track changes on devices that can actually map the display backlight
             // values into a physical brightness unit since the value provided by the API is in
             // nits and not using the arbitrary backlight units.
-            mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated);
+            final float powerFactor = mPowerRequest.lowPowerMode
+                    ? mPowerRequest.screenLowPowerBrightnessFactor
+                    : 1.0f;
+            mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+                    powerFactor, hadUserDataPoint,
+                    mAutomaticBrightnessController.isDefaultConfig());
         }
     }
 
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 8fa3318..401c05e 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -862,7 +862,8 @@
             }
             startTrackingJobLocked(jobStatus, toCancel);
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
-                    uId, null, jobStatus.getBatteryName(), 2);
+                    uId, null, jobStatus.getBatteryName(),
+                    StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -2206,12 +2207,7 @@
                     Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
                 }
                 synchronized (mLock) {
-                    // TODO: update to be more efficient once we can slice by source UID
-                    mJobs.forEachJob((JobStatus job) -> {
-                        if (job.getSourceUid() == uid) {
-                            job.setStandbyBucket(bucketIndex);
-                        }
-                    });
+                    mJobs.forEachJobForSourceUid(uid, job -> job.setStandbyBucket(bucketIndex));
                     onControllerStateChanged();
                 }
             });
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
new file mode 100644
index 0000000..52381b8
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
@@ -0,0 +1,298 @@
+/*
+ * 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.locksettings.recoverablekeystore.storage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides helper methods serialize and deserialize {@link KeyChainSnapshot}.
+ *
+ * <p> It is necessary since {@link android.os.Parcelable} is not designed for persistent storage.
+ *
+ * <p> For every list, length is stored before the elements.
+ *
+ */
+public class PersistentKeyChainSnapshot {
+    private static final int VERSION = 1;
+    private static final int NULL_LIST_LENGTH = -1;
+
+    private DataInputStream mInput;
+    private DataOutputStream mOut;
+    private ByteArrayOutputStream mOutStream;
+
+    @VisibleForTesting
+    PersistentKeyChainSnapshot() {
+    }
+
+    @VisibleForTesting
+    void initReader(byte[] input) {
+        mInput = new DataInputStream(new ByteArrayInputStream(input));
+    }
+
+    @VisibleForTesting
+    void initWriter() {
+        mOutStream = new ByteArrayOutputStream();
+        mOut = new DataOutputStream(mOutStream);
+    }
+
+    @VisibleForTesting
+    byte[] getOutput() {
+        return mOutStream.toByteArray();
+    }
+
+    /**
+     * Converts {@link KeyChainSnapshot} to its binary representation.
+     *
+     * @param snapshot The snapshot.
+     *
+     * @throws IOException if serialization failed.
+     */
+    public static byte[] serialize(@NonNull KeyChainSnapshot snapshot) throws IOException {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        writer.writeInt(VERSION);
+        writer.writeKeyChainSnapshot(snapshot);
+        return writer.getOutput();
+    }
+
+    /**
+     * deserializes {@link KeyChainSnapshot}.
+     *
+     * @input input - byte array produced by {@link serialize} method.
+     * @throws IOException if parsing failed.
+     */
+    public static @NonNull KeyChainSnapshot deserialize(@NonNull byte[] input)
+            throws IOException {
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(input);
+        try {
+            int version = reader.readInt();
+            if (version != VERSION) {
+                throw new IOException("Unsupported version " + version);
+            }
+            return reader.readKeyChainSnapshot();
+        } catch (IOException e) {
+            throw new IOException("Malformed KeyChainSnapshot", e);
+        }
+    }
+
+    /**
+     * Must be in sync with {@link KeyChainSnapshot.writeToParcel}
+     */
+    @VisibleForTesting
+    void writeKeyChainSnapshot(KeyChainSnapshot snapshot) throws IOException {
+        writeInt(snapshot.getSnapshotVersion());
+        writeProtectionParamsList(snapshot.getKeyChainProtectionParams());
+        writeBytes(snapshot.getEncryptedRecoveryKeyBlob());
+        writeKeysList(snapshot.getWrappedApplicationKeys());
+
+        writeInt(snapshot.getMaxAttempts());
+        writeLong(snapshot.getCounterId());
+        writeBytes(snapshot.getServerParams());
+        writeBytes(snapshot.getTrustedHardwarePublicKey());
+    }
+
+    @VisibleForTesting
+    KeyChainSnapshot readKeyChainSnapshot() throws IOException {
+        int snapshotVersion = readInt();
+        List<KeyChainProtectionParams> protectionParams = readProtectionParamsList();
+        byte[] encryptedRecoveryKey = readBytes();
+        List<WrappedApplicationKey> keysList = readKeysList();
+
+        int maxAttempts = readInt();
+        long conterId = readLong();
+        byte[] serverParams = readBytes();
+        byte[] trustedHardwarePublicKey = readBytes();
+
+        return new KeyChainSnapshot.Builder()
+                .setSnapshotVersion(snapshotVersion)
+                .setKeyChainProtectionParams(protectionParams)
+                .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
+                .setWrappedApplicationKeys(keysList)
+                .setMaxAttempts(maxAttempts)
+                .setCounterId(conterId)
+                .setServerParams(serverParams)
+                .setTrustedHardwarePublicKey(trustedHardwarePublicKey)
+                .build();
+    }
+
+    @VisibleForTesting
+    void writeProtectionParamsList(
+            @NonNull List<KeyChainProtectionParams> ProtectionParamsList) throws IOException {
+        writeInt(ProtectionParamsList.size());
+        for (KeyChainProtectionParams protectionParams : ProtectionParamsList) {
+            writeProtectionParams(protectionParams);
+        }
+    }
+
+    @VisibleForTesting
+    List<KeyChainProtectionParams> readProtectionParamsList() throws IOException {
+        int length = readInt();
+        List<KeyChainProtectionParams> result = new ArrayList<>(length);
+        for (int i = 0; i < length; i++) {
+            result.add(readProtectionParams());
+        }
+        return result;
+    }
+
+    /**
+     * Must be in sync with {@link KeyChainProtectionParams.writeToParcel}
+     */
+    @VisibleForTesting
+    void writeProtectionParams(@NonNull KeyChainProtectionParams protectionParams)
+            throws IOException {
+        if (!ArrayUtils.isEmpty(protectionParams.getSecret())) {
+            // Extra security check.
+            throw new RuntimeException("User generated secret should not be stored");
+        }
+        writeInt(protectionParams.getUserSecretType());
+        writeInt(protectionParams.getLockScreenUiFormat());
+        writeKeyDerivationParams(protectionParams.getKeyDerivationParams());
+        writeBytes(protectionParams.getSecret());
+    }
+
+    @VisibleForTesting
+    KeyChainProtectionParams readProtectionParams() throws IOException {
+        int userSecretType = readInt();
+        int lockScreenUiFormat = readInt();
+        KeyDerivationParams derivationParams = readKeyDerivationParams();
+        byte[] secret = readBytes();
+        return new KeyChainProtectionParams.Builder()
+                .setUserSecretType(userSecretType)
+                .setLockScreenUiFormat(lockScreenUiFormat)
+                .setKeyDerivationParams(derivationParams)
+                .setSecret(secret)
+                .build();
+    }
+
+    /**
+     * Must be in sync with {@link KeyDerivationParams.writeToParcel}
+     */
+    @VisibleForTesting
+    void writeKeyDerivationParams(@NonNull KeyDerivationParams Params) throws IOException {
+        writeInt(Params.getAlgorithm());
+        writeBytes(Params.getSalt());
+    }
+
+    @VisibleForTesting
+    KeyDerivationParams readKeyDerivationParams() throws IOException {
+        int algorithm = readInt();
+        byte[] salt = readBytes();
+        return KeyDerivationParams.createSha256Params(salt);
+    }
+
+    @VisibleForTesting
+    void writeKeysList(@NonNull List<WrappedApplicationKey> applicationKeys) throws IOException {
+        writeInt(applicationKeys.size());
+        for (WrappedApplicationKey keyEntry : applicationKeys) {
+            writeKeyEntry(keyEntry);
+        }
+    }
+
+    @VisibleForTesting
+    List<WrappedApplicationKey> readKeysList() throws IOException {
+        int length = readInt();
+        List<WrappedApplicationKey> result = new ArrayList<>(length);
+        for (int i = 0; i < length; i++) {
+            result.add(readKeyEntry());
+        }
+        return result;
+    }
+
+    /**
+     * Must be in sync with {@link WrappedApplicationKey.writeToParcel}
+     */
+    @VisibleForTesting
+    void writeKeyEntry(@NonNull WrappedApplicationKey keyEntry) throws IOException {
+        mOut.writeUTF(keyEntry.getAlias());
+        writeBytes(keyEntry.getEncryptedKeyMaterial());
+        writeBytes(keyEntry.getAccount());
+    }
+
+    @VisibleForTesting
+    WrappedApplicationKey readKeyEntry() throws IOException {
+        String alias = mInput.readUTF();
+        byte[] keyMaterial = readBytes();
+        byte[] account = readBytes();
+        return new WrappedApplicationKey.Builder()
+                .setAlias(alias)
+                .setEncryptedKeyMaterial(keyMaterial)
+                .setAccount(account)
+                .build();
+    }
+
+    @VisibleForTesting
+    void writeInt(int value) throws IOException {
+        mOut.writeInt(value);
+    }
+
+    @VisibleForTesting
+    int readInt() throws IOException {
+        return mInput.readInt();
+    }
+
+    @VisibleForTesting
+    void writeLong(long value) throws IOException {
+        mOut.writeLong(value);
+    }
+
+    @VisibleForTesting
+    long readLong() throws IOException {
+        return mInput.readLong();
+    }
+
+    @VisibleForTesting
+    void writeBytes(@Nullable byte[] value) throws IOException {
+        if (value == null) {
+            writeInt(NULL_LIST_LENGTH);
+            return;
+        }
+        writeInt(value.length);
+        mOut.write(value, 0, value.length);
+    }
+
+    /**
+     * Reads @code{byte[]} from current position. Converts {@code null} to an empty array.
+     */
+    @VisibleForTesting
+    @NonNull byte[] readBytes() throws IOException {
+        int length = readInt();
+        if (length == NULL_LIST_LENGTH) {
+            return new byte[]{};
+        }
+        byte[] result = new byte[length];
+        mInput.read(result, 0, result.length);
+        return result;
+    }
+}
+
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index d96671c..79fd496 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -28,7 +28,7 @@
  * Helper for creating the recoverable key database.
  */
 class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
-    private static final int DATABASE_VERSION = 1;
+    private static final int DATABASE_VERSION = 2;
     private static final String DATABASE_NAME = "recoverablekeystore.db";
 
     private static final String SQL_CREATE_KEYS_ENTRY =
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 7165e60..5f4e471 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -23,8 +23,10 @@
 import android.net.metrics.IpConnectivityLog;
 import android.os.Binder;
 import android.os.Process;
+import android.os.ResultReceiver;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -80,6 +82,7 @@
                     return;
                 }
                 try {
+                    mService.init();
                     mService.initIpConnectivityMetrics();
                     mService.startWatchlistLogging();
                 } catch (RemoteException e) {
@@ -127,6 +130,10 @@
         mIpConnectivityMetrics = ipConnectivityMetrics;
     }
 
+    private void init() {
+        mConfig.removeTestModeConfig();
+    }
+
     private void initIpConnectivityMetrics() {
         mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
                 ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
@@ -151,6 +158,22 @@
         }
     };
 
+    private boolean isCallerShell() {
+        final int callingUid = Binder.getCallingUid();
+        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+    }
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        if (!isCallerShell()) {
+            Slog.w(TAG, "Only shell is allowed to call network watchlist shell commands");
+            return;
+        }
+        (new NetworkWatchlistShellCommand(mContext)).exec(this, in, out, err, args, callback,
+                resultReceiver);
+    }
+
     @VisibleForTesting
     protected boolean startWatchlistLoggingImpl() throws RemoteException {
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
new file mode 100644
index 0000000..9533823
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkWatchlistManager;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+/**
+ * Network watchlist shell commands class, to provide a way to set temporary watchlist config for
+ * testing in shell, so CTS / GTS can use it to verify if watchlist feature is working properly.
+ */
+class NetworkWatchlistShellCommand extends ShellCommand {
+
+    final NetworkWatchlistManager mNetworkWatchlistManager;
+
+    NetworkWatchlistShellCommand(Context context) {
+        mNetworkWatchlistManager = new NetworkWatchlistManager(context);
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch(cmd) {
+                case "set-test-config":
+                    return runSetTestConfig();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    /**
+     * Method to get fd from input xml path, and set it as temporary watchlist config.
+     */
+    private int runSetTestConfig() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            final String configXmlPath = getNextArgRequired();
+            final ParcelFileDescriptor pfd = openFileForSystem(configXmlPath, "r");
+            if (pfd != null) {
+                final InputStream fileStream = new FileInputStream(pfd.getFileDescriptor());
+                WatchlistConfig.getInstance().setTestMode(fileStream);
+            }
+            pw.println("Success!");
+        } catch (RuntimeException | IOException ex) {
+            pw.println("Error: " + ex.toString());
+            return -1;
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("Network watchlist manager commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println("");
+        pw.println("  set-test-config your_watchlist_config.xml");
+        pw.println();
+        Intent.printIntentArgsHelp(pw , "");
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index 7387ad4..2714d5e 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net.watchlist;
 
+import android.os.FileUtils;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
@@ -32,6 +33,7 @@
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -50,6 +52,8 @@
     // Watchlist config that pushed by ConfigUpdater.
     private static final String NETWORK_WATCHLIST_DB_PATH =
             "/data/misc/network_watchlist/network_watchlist.xml";
+    private static final String NETWORK_WATCHLIST_DB_FOR_TEST_PATH =
+            "/data/misc/network_watchlist/network_watchlist_for_test.xml";
 
     // Hash for null / unknown config, a 32 byte array filled with content 0x00
     private static final byte[] UNKNOWN_CONFIG_HASH = new byte[32];
@@ -80,7 +84,7 @@
     private boolean mIsSecureConfig = true;
 
     private final static WatchlistConfig sInstance = new WatchlistConfig();
-    private final File mXmlFile;
+    private File mXmlFile;
 
     private volatile CrcShaDigests mDomainDigests;
     private volatile CrcShaDigests mIpDigests;
@@ -232,7 +236,38 @@
         return UNKNOWN_CONFIG_HASH;
     }
 
+    /**
+     * This method will copy temporary test config and temporary override network watchlist config
+     * in memory. When device is rebooted, temporary test config will be removed, and system will
+     * use back the original watchlist config.
+     * Also, as temporary network watchlist config is not secure, we will mark it as insecure
+     * config and will be applied to testOnly applications only.
+     */
+    public void setTestMode(InputStream testConfigInputStream) throws IOException {
+        Log.i(TAG, "Setting watchlist testing config");
+        // Copy test config
+        FileUtils.copyToFileOrThrow(testConfigInputStream,
+                new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH));
+        // Mark config as insecure, so it will be applied to testOnly applications only
+        mIsSecureConfig = false;
+        // Reload watchlist config using test config file
+        mXmlFile = new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH);
+        reloadConfig();
+    }
+
+    public void removeTestModeConfig() {
+        try {
+            final File f = new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH);
+            if (f.exists()) {
+                f.delete();
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to delete test config");
+        }
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Watchlist config hash: " + HexDump.toHexString(getWatchlistConfigHash()));
         pw.println("Domain CRC32 digest list:");
         if (mDomainDigests != null) {
             mDomainDigests.crc32Digests.dump(fd, pw, args);
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index 3b6d59e..c4de4ac 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -118,6 +118,25 @@
     }
 
     /**
+     * Return if a given package has testOnly is true.
+     */
+    private boolean isPackageTestOnly(int uid) {
+        final ApplicationInfo ai;
+        try {
+            final String[] packageNames = mPm.getPackagesForUid(uid);
+            if (packageNames == null || packageNames.length == 0) {
+                Slog.e(TAG, "Couldn't find package: " + packageNames);
+                return false;
+            }
+            ai = mPm.getApplicationInfo(packageNames[0],0);
+        } catch (NameNotFoundException e) {
+            // Should not happen.
+            return false;
+        }
+        return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
+    }
+
+     /**
      * Report network watchlist records if we collected enough data.
      */
     public void reportWatchlistIfNecessary() {
@@ -146,16 +165,21 @@
         }
         final String cncDomain = searchAllSubDomainsInWatchlist(hostname);
         if (cncDomain != null) {
-            insertRecord(getDigestFromUid(uid), cncDomain, timestamp);
+            insertRecord(uid, cncDomain, timestamp);
         } else {
             final String cncIp = searchIpInWatchlist(ipAddresses);
             if (cncIp != null) {
-                insertRecord(getDigestFromUid(uid), cncIp, timestamp);
+                insertRecord(uid, cncIp, timestamp);
             }
         }
     }
 
-    private boolean insertRecord(byte[] digest, String cncHost, long timestamp) {
+    private boolean insertRecord(int uid, String cncHost, long timestamp) {
+        if (!mConfig.isConfigSecure() && !isPackageTestOnly(uid)) {
+            // Skip package if config is not secure and package is not TestOnly app.
+            return true;
+        }
+        final byte[] digest = getDigestFromUid(uid);
         final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
         tryAggregateRecords();
         return result;
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index c73b0cf..4b577bb 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -141,20 +141,19 @@
     }
 
     /**
-     * Aggregate the records in database, and return a rappor encoded result.
+     * Aggregate all records before most recent local midnight in database, and return a
+     * rappor encoded result.
      */
     public AggregatedResult getAggregatedRecords() {
-        final long twoDaysBefore = getTwoDaysBeforeTimestamp();
-        final long yesterday = getYesterdayTimestamp();
-        final String selectStatement = WhiteListReportContract.TIMESTAMP + " >= ? AND " +
-                WhiteListReportContract.TIMESTAMP + " <= ?";
+        final long lastMidnightTime = getLastMidnightTime();
+        final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?";
 
         final SQLiteDatabase db = getReadableDatabase();
         Cursor c = null;
         try {
             c = db.query(true /* distinct */,
                     WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
-                    new String[]{"" + twoDaysBefore, "" + yesterday}, null, null,
+                    new String[]{"" + lastMidnightTime}, null, null,
                     null, null);
             if (c == null) {
                 return null;
@@ -182,23 +181,19 @@
     }
 
     /**
-     * Remove all the records before yesterday.
+     * Remove all the records before most recent local midnight.
      *
      * @return True if success.
      */
     public boolean cleanup() {
         final SQLiteDatabase db = getWritableDatabase();
-        final long twoDaysBefore = getTwoDaysBeforeTimestamp();
-        final String clause = WhiteListReportContract.TIMESTAMP + "< " + twoDaysBefore;
+        final long midnightTime = getLastMidnightTime();
+        final String clause = WhiteListReportContract.TIMESTAMP + "< " + midnightTime;
         return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
     }
 
-    static long getTwoDaysBeforeTimestamp() {
-        return getMidnightTimestamp(2);
-    }
-
-    static long getYesterdayTimestamp() {
-        return getMidnightTimestamp(1);
+    static long getLastMidnightTime() {
+        return getMidnightTimestamp(0);
     }
 
     static long getMidnightTimestamp(int daysBefore) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 502760a..fd435f9 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -771,7 +771,7 @@
      * Called whenever packages change, the user switches, or the secure setting
      * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
      */
-    private void rebindServices(boolean forceRebind) {
+    protected void rebindServices(boolean forceRebind) {
         if (DEBUG) Slog.d(TAG, "rebindServices");
         final int[] userIds = mUserProfiles.getCurrentProfileIds();
         final int nUserIds = userIds.length;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9865e35..b25124a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -111,6 +111,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.IDeviceIdleController;
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
@@ -286,6 +287,7 @@
     private AlarmManager mAlarmManager;
     private ICompanionDeviceManager mCompanionManager;
     private AccessibilityManager mAccessibilityManager;
+    private IDeviceIdleController mDeviceIdleController;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -659,6 +661,7 @@
 
         @Override
         public void onNotificationClick(int callingUid, int callingPid, String key) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
@@ -683,6 +686,7 @@
         @Override
         public void onNotificationActionClick(int callingUid, int callingPid, String key,
                 int actionIndex) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
@@ -812,6 +816,7 @@
 
         @Override
         public void onNotificationDirectReplied(String key) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -1280,6 +1285,8 @@
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
 
         mHandler = new WorkerHandler(looper);
         mRankingThread.start();
@@ -1533,6 +1540,15 @@
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private void exitIdle() {
+        try {
+            if (mDeviceIdleController != null) {
+                mDeviceIdleController.exitIdle("notification interaction");
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
     private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
             boolean fromListener) {
         if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
@@ -2875,6 +2891,7 @@
         // Backup/restore interface
         @Override
         public byte[] getBackupPayload(int user) {
+            checkCallerIsSystem();
             if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
             //TODO: http://b/22388012
             if (user != UserHandle.USER_SYSTEM) {
@@ -2895,6 +2912,7 @@
 
         @Override
         public void applyRestore(byte[] payload, int user) {
+            checkCallerIsSystem();
             if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
                     + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
             if (payload == null) {
@@ -5671,6 +5689,12 @@
             mListeners.unregisterService(removed.service, removed.userid);
         }
 
+        @Override
+        public void onUserUnlocked(int user) {
+            if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+            rebindServices(true);
+        }
+
         public void onNotificationEnqueued(final NotificationRecord r) {
             final StatusBarNotification sbn = r.sbn;
             TrimCache trimCache = new TrimCache(sbn);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index af20cd7..30088dd 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -44,6 +44,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
@@ -51,6 +52,7 @@
 import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -295,25 +297,26 @@
                 continue;
             }
 
+            String cookieName = currentCookieFile.getName();
+            String currentCookieSha256 =
+                    cookieName.substring(INSTANT_APP_COOKIE_FILE_PREFIX.length(),
+                            cookieName.length() - INSTANT_APP_COOKIE_FILE_SIFFIX.length());
+
             // Before we used only the first signature to compute the SHA 256 but some
             // apps could be singed by multiple certs and the cert order is undefined.
             // We prefer the modern computation procedure where all certs are taken
             // into account but also allow the value from the old computation to avoid
             // data loss.
-            final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
-                    pkg.mSigningDetails.signatures);
-            final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest(
-                    signaturesSha256Digests);
-
-            // We prefer a match based on all signatures
-            if (currentCookieFile.equals(computeInstantCookieFile(pkg.packageName,
-                    signaturesSha256Digest, userId))) {
+            if (pkg.mSigningDetails.checkCapability(currentCookieSha256,
+                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                 return;
             }
 
-            // For backwards compatibility we accept match based on first signature
-            if (pkg.mSigningDetails.signatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
-                    pkg.packageName, signaturesSha256Digests[0], userId))) {
+            // For backwards compatibility we accept match based on first signature only in the case
+            // of multiply-signed packagse
+            final String[] signaturesSha256Digests =
+                    PackageUtils.computeSignaturesSha256Digests(pkg.mSigningDetails.signatures);
+            if (signaturesSha256Digests[0].equals(currentCookieSha256)) {
                 return;
             }
 
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 55212cc..af446ba 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -51,8 +51,8 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.pm.EphemeralResolverConnection.ConnectionException;
-import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback;
+import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
+import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -66,7 +66,7 @@
 
 /** @hide */
 public abstract class InstantAppResolver {
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
     private static final String TAG = "PackageManager";
 
     private static final int RESOLUTION_SUCCESS = 0;
@@ -117,10 +117,10 @@
     }
 
     public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
-            EphemeralResolverConnection connection, InstantAppRequest requestObj) {
+            InstantAppResolverConnection connection, InstantAppRequest requestObj) {
         final long startTime = System.currentTimeMillis();
         final String token = UUID.randomUUID().toString();
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Log.d(TAG, "[" + token + "] Phase1; resolving");
         }
         final Intent origIntent = requestObj.origIntent;
@@ -152,7 +152,7 @@
             logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
                     resolutionStatus);
         }
-        if (DEBUG_EPHEMERAL && resolveInfo == null) {
+        if (DEBUG_INSTANT && resolveInfo == null) {
             if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
                 Log.d(TAG, "[" + token + "] Phase1; bind timed out");
             } else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) {
@@ -173,11 +173,11 @@
     }
 
     public static void doInstantAppResolutionPhaseTwo(Context context,
-            EphemeralResolverConnection connection, InstantAppRequest requestObj,
+            InstantAppResolverConnection connection, InstantAppRequest requestObj,
             ActivityInfo instantAppInstaller, Handler callbackHandler) {
         final long startTime = System.currentTimeMillis();
         final String token = requestObj.responseObj.token;
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Log.d(TAG, "[" + token + "] Phase2; resolving");
         }
         final Intent origIntent = requestObj.origIntent;
@@ -234,7 +234,7 @@
             }
             logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
                     resolutionStatus);
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
                     Log.d(TAG, "[" + token + "] Phase2; bind timed out");
                 } else {
@@ -268,10 +268,14 @@
                 | Intent.FLAG_ACTIVITY_NO_HISTORY
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         if (token != null) {
+            // TODO(b/72700831): remove populating old extra
             intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
+            intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token);
         }
         if (origIntent.getData() != null) {
+            // TODO(b/72700831): remove populating old extra
             intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
+            intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost());
         }
         intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
         intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent);
@@ -305,8 +309,10 @@
                                             | PendingIntent.FLAG_ONE_SHOT
                                             | PendingIntent.FLAG_IMMUTABLE,
                                     null /*bOptions*/, userId);
-                    intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE,
-                            new IntentSender(failureIntentTarget));
+                    IntentSender failureSender = new IntentSender(failureIntentTarget);
+                    // TODO(b/72700831): remove populating old extra
+                    intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, failureSender);
+                    intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender);
                 } catch (RemoteException ignore) { /* ignore; same process */ }
             }
 
@@ -323,8 +329,10 @@
                                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
                                         | PendingIntent.FLAG_IMMUTABLE,
                                 null /*bOptions*/, userId);
-                intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS,
-                        new IntentSender(successIntentTarget));
+                IntentSender successSender = new IntentSender(successIntentTarget);
+                // TODO(b/72700831): remove populating old extra
+                intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, successSender);
+                intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender);
             } catch (RemoteException ignore) { /* ignore; same process */ }
             if (verificationBundle != null) {
                 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
@@ -444,13 +452,13 @@
                 return null;
             }
             // No filters; we need to start phase two
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Log.d(TAG, "No app filters; go to phase 2");
             }
             return Collections.emptyList();
         }
-        final PackageManagerService.EphemeralIntentResolver instantAppResolver =
-                new PackageManagerService.EphemeralIntentResolver();
+        final PackageManagerService.InstantAppIntentResolver instantAppResolver =
+                new PackageManagerService.InstantAppIntentResolver();
         for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
             final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
             final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
@@ -481,11 +489,11 @@
                 instantAppResolver.queryIntent(
                         origIntent, resolvedType, false /*defaultOnly*/, userId);
         if (!matchedResolveInfoList.isEmpty()) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
             }
             return matchedResolveInfoList;
-        } else if (DEBUG_EPHEMERAL) {
+        } else if (DEBUG_INSTANT) {
             Log.d(TAG, "[" + token + "] No matches found"
                     + " package: " + instantAppInfo.getPackageName()
                     + ", versionCode: " + instantAppInfo.getVersionCode());
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
similarity index 90%
rename from services/core/java/com/android/server/pm/EphemeralResolverConnection.java
rename to services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index 6d6c960..a9ee411 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -37,34 +37,29 @@
 import android.util.TimedRemoteCaller;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.TransferPipe;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.TimeoutException;
 
 /**
- * Represents a remote ephemeral resolver. It is responsible for binding to the remote
+ * Represents a remote instant app resolver. It is responsible for binding to the remote
  * service and handling all interactions in a timely manner.
  * @hide
  */
-final class EphemeralResolverConnection implements DeathRecipient {
+final class InstantAppResolverConnection implements DeathRecipient {
     private static final String TAG = "PackageManager";
     // This is running in a critical section and the timeout must be sufficiently low
     private static final long BIND_SERVICE_TIMEOUT_MS =
             Build.IS_ENG ? 500 : 300;
     private static final long CALL_SERVICE_TIMEOUT_MS =
             Build.IS_ENG ? 200 : 100;
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
 
     private final Object mLock = new Object();
-    private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
-            new GetEphemeralResolveInfoCaller();
+    private final GetInstantAppResolveInfoCaller mGetInstantAppResolveInfoCaller =
+            new GetInstantAppResolveInfoCaller();
     private final ServiceConnection mServiceConnection = new MyServiceConnection();
     private final Context mContext;
     /** Intent used to bind to the service */
@@ -79,7 +74,7 @@
     @GuardedBy("mLock")
     private IInstantAppResolver mRemoteInstance;
 
-    public EphemeralResolverConnection(
+    public InstantAppResolverConnection(
             Context context, ComponentName componentName, String action) {
         mContext = context;
         mIntent = new Intent(action).setComponent(componentName);
@@ -98,8 +93,8 @@
                 throw new ConnectionException(ConnectionException.FAILURE_INTERRUPTED);
             }
             try {
-                return mGetEphemeralResolveInfoCaller
-                        .getEphemeralResolveInfoList(target, sanitizedIntent, hashPrefix, token);
+                return mGetInstantAppResolveInfoCaller
+                        .getInstantAppResolveInfoList(target, sanitizedIntent, hashPrefix, token);
             } catch (TimeoutException e) {
                 throw new ConnectionException(ConnectionException.FAILURE_CALL);
             } catch (RemoteException ignore) {
@@ -171,7 +166,7 @@
 
             if (mBindState == STATE_PENDING) {
                 // there is a pending bind, let's see if we can use it.
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
                 }
                 try {
@@ -188,7 +183,7 @@
             if (mBindState == STATE_BINDING) {
                 // someone was binding when we called bind(), or they raced ahead while we were
                 // waiting in the PENDING case; wait for their result instead. Last chance!
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.i(TAG, "[" + token + "] Another thread is binding; waiting for connection");
                 }
                 waitForBindLocked(token);
@@ -206,12 +201,12 @@
         IInstantAppResolver instance = null;
         try {
             if (doUnbind) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
                 }
                 mContext.unbindService(mServiceConnection);
             }
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.v(TAG, "[" + token + "] Binding to instant app resolver");
             }
             final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
@@ -247,7 +242,7 @@
 
     @Override
     public void binderDied() {
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Slog.d(TAG, "Binder to instant app resolver died");
         }
         synchronized (mLock) {
@@ -286,7 +281,7 @@
     private final class MyServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.d(TAG, "Connected to instant app resolver");
             }
             synchronized (mLock) {
@@ -295,7 +290,7 @@
                     mBindState = STATE_IDLE;
                 }
                 try {
-                    service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/);
+                    service.linkToDeath(InstantAppResolverConnection.this, 0 /*flags*/);
                 } catch (RemoteException e) {
                     handleBinderDiedLocked();
                 }
@@ -305,7 +300,7 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.d(TAG, "Disconnected from instant app resolver");
             }
             synchronized (mLock) {
@@ -314,11 +309,11 @@
         }
     }
 
-    private static final class GetEphemeralResolveInfoCaller
+    private static final class GetInstantAppResolveInfoCaller
             extends TimedRemoteCaller<List<InstantAppResolveInfo>> {
         private final IRemoteCallback mCallback;
 
-        public GetEphemeralResolveInfoCaller() {
+        public GetInstantAppResolveInfoCaller() {
             super(CALL_SERVICE_TIMEOUT_MS);
             mCallback = new IRemoteCallback.Stub() {
                     @Override
@@ -333,7 +328,7 @@
             };
         }
 
-        public List<InstantAppResolveInfo> getEphemeralResolveInfoList(
+        public List<InstantAppResolveInfo> getInstantAppResolveInfoList(
                 IInstantAppResolver target, Intent sanitizedIntent,  int hashPrefix[], String token)
                         throws RemoteException, TimeoutException {
             final int sequence = onBeforeRemoteCall();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fdb157e..89fbd17 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -108,8 +108,6 @@
 import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
 import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasCertificate;
-import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasSha256Certificate;
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
@@ -246,6 +244,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Base64;
+import android.util.ByteStringUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.ExceptionUtils;
@@ -424,7 +423,7 @@
     public static final boolean DEBUG_DEXOPT = false;
 
     private static final boolean DEBUG_ABI_SELECTION = false;
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_TRIAGED_MISSING = false;
     private static final boolean DEBUG_APP_DATA = false;
 
@@ -583,6 +582,7 @@
         sBrowserIntent.setAction(Intent.ACTION_VIEW);
         sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE);
         sBrowserIntent.setData(Uri.parse("http:"));
+        sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL);
     }
 
     /**
@@ -981,7 +981,7 @@
     private int mIntentFilterVerificationToken = 0;
 
     /** The service connection to the ephemeral resolver */
-    final EphemeralResolverConnection mInstantAppResolverConnection;
+    final InstantAppResolverConnection mInstantAppResolverConnection;
     /** Component used to show resolver settings for Instant Apps */
     final ComponentName mInstantAppResolverSettingsComponent;
 
@@ -3149,10 +3149,10 @@
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
                 }
-                mInstantAppResolverConnection = new EphemeralResolverConnection(
+                mInstantAppResolverConnection = new InstantAppResolverConnection(
                         mContext, instantAppResolverComponent.first,
                         instantAppResolverComponent.second);
                 mInstantAppResolverSettingsComponent =
@@ -3532,7 +3532,7 @@
         final String[] packageArray =
                 mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
         if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
             }
             return null;
@@ -3547,19 +3547,9 @@
         final Intent resolverIntent = new Intent(actionName);
         List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
                 resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
-        // temporarily look for the old action
-        if (resolvers.size() == 0) {
-            if (DEBUG_EPHEMERAL) {
-                Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
-            }
-            actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
-            resolverIntent.setAction(actionName);
-            resolvers = queryIntentServicesInternal(resolverIntent, null,
-                    resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
-        }
         final int N = resolvers.size();
         if (N == 0) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
             }
             return null;
@@ -3575,20 +3565,20 @@
 
             final String packageName = info.serviceInfo.packageName;
             if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
                             + " pkg: " + packageName + ", info:" + info);
                 }
                 continue;
             }
 
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.v(TAG, "Ephemeral resolver found;"
                         + " pkg: " + packageName + ", info:" + info);
             }
             return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
         }
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Slog.v(TAG, "Ephemeral resolver NOT found");
         }
         return null;
@@ -3598,11 +3588,9 @@
         String[] orderedActions = Build.IS_ENG
                 ? new String[]{
                         Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
-                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE,
-                        Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE}
+                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
                 : new String[]{
-                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE,
-                        Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE};
+                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE};
 
         final int resolveFlags =
                 MATCH_DIRECT_BOOT_AWARE
@@ -3618,7 +3606,7 @@
             matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                     resolveFlags, UserHandle.USER_SYSTEM);
             if (matches.isEmpty()) {
-                if (DEBUG_EPHEMERAL) {
+                if (DEBUG_INSTANT) {
                     Slog.d(TAG, "Instant App installer not found with " + action);
                 }
             } else {
@@ -3656,15 +3644,6 @@
         final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
         List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
                 UserHandle.USER_SYSTEM);
-        // temporarily look for the old action
-        if (matches.isEmpty()) {
-            if (DEBUG_EPHEMERAL) {
-                Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
-            }
-            intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
-            matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
-                    UserHandle.USER_SYSTEM);
-        }
         if (matches.isEmpty()) {
             return null;
         }
@@ -5546,9 +5525,9 @@
             }
             switch (type) {
                 case CERT_INPUT_RAW_X509:
-                    return signingDetailsHasCertificate(certificate, p.mSigningDetails);
+                    return p.mSigningDetails.hasCertificate(certificate);
                 case CERT_INPUT_SHA256:
-                    return signingDetailsHasSha256Certificate(certificate, p.mSigningDetails);
+                    return p.mSigningDetails.hasSha256Certificate(certificate);
                 default:
                     return false;
             }
@@ -5587,9 +5566,9 @@
             }
             switch (type) {
                 case CERT_INPUT_RAW_X509:
-                    return signingDetailsHasCertificate(certificate, signingDetails);
+                    return signingDetails.hasCertificate(certificate);
                 case CERT_INPUT_SHA256:
-                    return signingDetailsHasSha256Certificate(certificate, signingDetails);
+                    return signingDetails.hasSha256Certificate(certificate);
                 default:
                     return false;
             }
@@ -5988,7 +5967,7 @@
                     || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
                 return false;
             }
-        } else if (intent.getData() == null) {
+        } else if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
             return false;
         }
         // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
@@ -6007,7 +5986,7 @@
                         final int status = (int) (packedStatus >> 32);
                         if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
                             || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
-                            if (DEBUG_EPHEMERAL) {
+                            if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "DENY instant app;"
                                     + " pkg: " + packageName + ", status: " + status);
                             }
@@ -6015,7 +5994,7 @@
                         }
                     }
                     if (ps.getInstantApp(userId)) {
-                        if (DEBUG_EPHEMERAL) {
+                        if (DEBUG_INSTANT) {
                             Slog.v(TAG, "DENY instant app installed;"
                                     + " pkg: " + packageName);
                         }
@@ -6651,7 +6630,7 @@
                         // there's a local instant application installed, but, the user has
                         // chosen to never use it; skip resolution and don't acknowledge
                         // an instant application is even available
-                        if (DEBUG_EPHEMERAL) {
+                        if (DEBUG_INSTANT) {
                             Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
                         }
                         blockResolution = true;
@@ -6659,7 +6638,7 @@
                     } else {
                         // we have a locally installed instant application; skip resolution
                         // but acknowledge there's an instant application available
-                        if (DEBUG_EPHEMERAL) {
+                        if (DEBUG_INSTANT) {
                             Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
                         }
                         localInstantApp = info;
@@ -6717,7 +6696,7 @@
         // make sure this resolver is the default
         ephemeralInstaller.isDefault = true;
         ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
         }
 
@@ -7604,7 +7583,7 @@
                                 info.serviceInfo.splitName)) {
                     // requested service is defined in a split that hasn't been installed yet.
                     // add the installer to the resolve list
-                    if (DEBUG_EPHEMERAL) {
+                    if (DEBUG_INSTANT) {
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
                     final ResolveInfo installerInfo = new ResolveInfo(
@@ -7726,7 +7705,7 @@
                                 info.providerInfo.splitName)) {
                     // requested provider is defined in a split that hasn't been installed yet.
                     // add the installer to the resolve list
-                    if (DEBUG_EPHEMERAL) {
+                    if (DEBUG_INSTANT) {
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
                     final ResolveInfo installerInfo = new ResolveInfo(
@@ -8367,13 +8346,11 @@
     }
 
     private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg,
-            boolean forceCollect) throws PackageManagerException {
+            boolean forceCollect, boolean skipVerify) throws PackageManagerException {
         // When upgrading from pre-N MR1, verify the package time stamp using the package
         // directory and not the APK file.
         final long lastModifiedTime = mIsPreNMR1Upgrade
                 ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
-        // Note that currently skipVerify skips verification on both base and splits for simplicity.
-        final boolean skipVerify = forceCollect && canSkipFullPackageVerification(pkg);
         if (ps != null && !forceCollect
                 && ps.codePathString.equals(pkg.codePath)
                 && ps.timeStamp == lastModifiedTime
@@ -8746,21 +8723,24 @@
         }
 
         // Verify certificates against what was last scanned. If it is an updated priv app, we will
-        // force re-collecting certificate. Full apk verification will happen unless apk verity is
-        // set up for the file. In that case, only small part of the apk is verified upfront.
+        // force re-collecting certificate.
         final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
                 disabledPkgSetting);
-        collectCertificatesLI(pkgSetting, pkg, forceCollect);
+        // Full APK verification can be skipped during certificate collection, only if the file is
+        // in verified partition, or can be verified on access (when apk verity is enabled). In both
+        // cases, only data in Signing Block is verified instead of the whole file.
+        final boolean skipVerify = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) ||
+                (forceCollect && canSkipFullPackageVerification(pkg));
+        collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
 
         boolean shouldHideSystemApp = false;
         // A new application appeared on /system, but, we already have a copy of
         // the application installed on /data.
         if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
                 && !pkgSetting.isSystem()) {
-            // if the signatures don't match, wipe the installed application and its data
-            if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
-                    pkg.mSigningDetails.signatures)
-                            != PackageManager.SIGNATURE_MATCH) {
+
+            if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
+                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                 logCriticalInfo(Log.WARN,
                         "System package signature mismatch;"
                         + " name: " + pkgSetting.name);
@@ -9725,34 +9705,51 @@
                     }
 
                     final String[] expectedCertDigests = requiredCertDigests[i];
-                    // For apps targeting O MR1 we require explicit enumeration of all certs.
-                    final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
-                            ? PackageUtils.computeSignaturesSha256Digests(
-                            libPkg.mSigningDetails.signatures)
-                            : PackageUtils.computeSignaturesSha256Digests(
-                                    new Signature[]{libPkg.mSigningDetails.signatures[0]});
 
-                    // Take a shortcut if sizes don't match. Note that if an app doesn't
-                    // target O we don't parse the "additional-certificate" tags similarly
-                    // how we only consider all certs only for apps targeting O (see above).
-                    // Therefore, the size check is safe to make.
-                    if (expectedCertDigests.length != libCertDigests.length) {
-                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                "Package " + packageName + " requires differently signed" +
-                                        " static shared library; failing!");
-                    }
 
-                    // Use a predictable order as signature order may vary
-                    Arrays.sort(libCertDigests);
-                    Arrays.sort(expectedCertDigests);
+                    if (expectedCertDigests.length > 1) {
 
-                    final int certCount = libCertDigests.length;
-                    for (int j = 0; j < certCount; j++) {
-                        if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+                        // For apps targeting O MR1 we require explicit enumeration of all certs.
+                        final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
+                                ? PackageUtils.computeSignaturesSha256Digests(
+                                libPkg.mSigningDetails.signatures)
+                                : PackageUtils.computeSignaturesSha256Digests(
+                                        new Signature[]{libPkg.mSigningDetails.signatures[0]});
+
+                        // Take a shortcut if sizes don't match. Note that if an app doesn't
+                        // target O we don't parse the "additional-certificate" tags similarly
+                        // how we only consider all certs only for apps targeting O (see above).
+                        // Therefore, the size check is safe to make.
+                        if (expectedCertDigests.length != libCertDigests.length) {
                             throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                     "Package " + packageName + " requires differently signed" +
                                             " static shared library; failing!");
                         }
+
+                        // Use a predictable order as signature order may vary
+                        Arrays.sort(libCertDigests);
+                        Arrays.sort(expectedCertDigests);
+
+                        final int certCount = libCertDigests.length;
+                        for (int j = 0; j < certCount; j++) {
+                            if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                        "Package " + packageName + " requires differently signed" +
+                                                " static shared library; failing!");
+                            }
+                        }
+                    } else {
+
+                        // lib signing cert could have rotated beyond the one expected, check to see
+                        // if the new one has been blessed by the old
+                        if (!libPkg.mSigningDetails.hasSha256Certificate(
+                                ByteStringUtils.fromHexToByteArray(expectedCertDigests[0]))) {
+                            throw new PackageManagerException(
+                                    INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                    "Package " + packageName + " requires differently signed" +
+                                            " static shared library; failing!");
+                        }
                     }
                 }
 
@@ -9988,8 +9985,7 @@
                 // priv-apps.
                 synchronized (mPackages) {
                     PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
-                    if (!pkg.packageName.equals("android")
-                            && (compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
+                    if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
                                 pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
                         scanFlags |= SCAN_AS_PRIVILEGED;
                     }
@@ -10169,6 +10165,15 @@
                 // We just determined the app is signed correctly, so bring
                 // over the latest parsed certs.
                 pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
+
+
+                // if this is is a sharedUser, check to see if the new package is signed by a newer
+                // signing certificate than the existing one, and if so, copy over the new details
+                if (signatureCheckPs.sharedUser != null
+                        && pkg.mSigningDetails.hasAncestor(
+                                signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
+                    signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
+                }
             } catch (PackageManagerException e) {
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     throw e;
@@ -10195,6 +10200,13 @@
                 String msg = "System package " + pkg.packageName
                         + " signature changed; retaining data.";
                 reportSettingsProblem(Log.WARN, msg);
+            } catch (IllegalArgumentException e) {
+
+                // should never happen: certs matched when checking, but not when comparing
+                // old to new for sharedUser
+                throw new PackageManagerException(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        "Signing certificates comparison made on incomparable signing details"
+                        + " but somehow passed verifySignatures!");
             }
         }
 
@@ -10440,7 +10452,20 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
         }
 
-        SELinuxMMAC.assignSeInfoValue(pkg);
+        // SELinux sandboxes become more restrictive as targetSdkVersion increases.
+        // To ensure that apps with sharedUserId are placed in the same selinux domain
+        // without breaking any assumptions about access, put them into the least
+        // restrictive targetSdkVersion=25 domain.
+        // TODO(b/72290969): Base this on the actual targetSdkVersion(s) of the apps within the
+        // sharedUserSetting, instead of defaulting to the least restrictive domain.
+        final int targetSdk = (sharedUserSetting != null) ? 25
+                : pkg.applicationInfo.targetSdkVersion;
+        // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
+        // They currently can be if the sharedUser apps are signed with the platform key.
+        final boolean isPrivileged = (sharedUserSetting != null) ?
+            sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+
+        SELinuxMMAC.assignSeInfoValue(pkg, isPrivileged, targetSdk);
 
         pkg.mExtras = pkgSetting;
         pkg.applicationInfo.processName = fixProcessName(
@@ -11757,14 +11782,14 @@
 
     private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
         if (installerActivity == null) {
-            if (DEBUG_EPHEMERAL) {
+            if (DEBUG_INSTANT) {
                 Slog.d(TAG, "Clear ephemeral installer activity");
             }
             mInstantAppInstallerActivity = null;
             return;
         }
 
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             Slog.d(TAG, "Set ephemeral installer activity: "
                     + installerActivity.getComponentName());
         }
@@ -13224,7 +13249,7 @@
         private int mFlags;
     }
 
-    static final class EphemeralIntentResolver
+    static final class InstantAppIntentResolver
             extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
             AuxiliaryResolveInfo.AuxiliaryFilter> {
         /**
@@ -13594,7 +13619,7 @@
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user,
             PackageParser.SigningDetails signingDetails) {
-        if (DEBUG_EPHEMERAL) {
+        if (DEBUG_INSTANT) {
             if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
                 Slog.d(TAG, "Ephemeral install of " + packageName);
             }
@@ -15078,7 +15103,7 @@
                 pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                         packageAbiOverride);
 
-                if (DEBUG_EPHEMERAL && ephemeral) {
+                if (DEBUG_INSTANT && ephemeral) {
                     Slog.v(TAG, "pkgLite for install: " + pkgLite);
                 }
 
@@ -15145,7 +15170,7 @@
                             installFlags |= PackageManager.INSTALL_EXTERNAL;
                             installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
-                            if (DEBUG_EPHEMERAL) {
+                            if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                             }
                             installFlags |= PackageManager.INSTALL_INSTANT_APP;
@@ -16061,10 +16086,10 @@
                     return;
                 }
             } else {
+
                 // default to original signature matching
-                if (compareSignatures(oldPackage.mSigningDetails.signatures,
-                        pkg.mSigningDetails.signatures)
-                        != PackageManager.SIGNATURE_MATCH) {
+                if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
+                        PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                     res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "New package has a different signature: " + pkgName);
                     return;
@@ -17058,9 +17083,25 @@
                                     sourcePackageSetting, scanFlags))) {
                         sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                     } else {
-                        sigsOk = compareSignatures(
-                                sourcePackageSetting.signatures.mSigningDetails.signatures,
-                                pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH;
+
+                        // in the event of signing certificate rotation, we need to see if the
+                        // package's certificate has rotated from the current one, or if it is an
+                        // older certificate with which the current is ok with sharing permissions
+                        if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
+                                        pkg.mSigningDetails,
+                                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                            sigsOk = true;
+                        } else if (pkg.mSigningDetails.checkCapability(
+                                        sourcePackageSetting.signatures.mSigningDetails,
+                                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+
+                            // the scanned package checks out, has signing certificate rotation
+                            // history, and is newer; bring it over
+                            sourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;
+                            sigsOk = true;
+                        } else {
+                            sigsOk = false;
+                        }
                     }
                     if (!sigsOk) {
                         // If the owning package is the system itself, we log but allow
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 76c199b..5060c4d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -531,18 +531,29 @@
             // migrate the old signatures to the new scheme
             packageSignatures.mSigningDetails = parsedSignatures;
             return true;
+        } else if (parsedSignatures.hasPastSigningCertificates()) {
+
+            // well this sucks: the parsed package has probably rotated signing certificates, but
+            // we don't have enough information to determine if the new signing certificate was
+            // blessed by the old one
+            logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
+                    + "certificate chain. Unable to install newer version with rotated signing "
+                    + "certificate.");
         }
         return false;
     }
 
-    private static boolean matchSignaturesRecover(String packageName,
-            Signature[] existingSignatures, Signature[] parsedSignatures) {
+    private static boolean matchSignaturesRecover(
+            String packageName,
+            PackageParser.SigningDetails existingSignatures,
+            PackageParser.SigningDetails parsedSignatures,
+            @PackageParser.SigningDetails.CertCapabilities int flags) {
         String msg = null;
         try {
-            if (Signature.areEffectiveMatch(existingSignatures, parsedSignatures)) {
-                logCriticalInfo(Log.INFO,
-                        "Recovered effectively matching certificates for " + packageName);
-                return true;
+            if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
+                logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
+                        + packageName);
+                    return true;
             }
         } catch (CertificateException e) {
             msg = e.getMessage();
@@ -563,9 +574,11 @@
             PackageSetting disabledPkgSetting) {
         try {
             PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
-            if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
-                        disabledPkgSetting.signatures.mSigningDetails.signatures)
-                    != PackageManager.SIGNATURE_MATCH) {
+            if (pkgSetting.signatures.mSigningDetails.checkCapability(
+                    disabledPkgSetting.signatures.mSigningDetails,
+                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+                return true;
+            } else {
                 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
                         pkgSetting.name);
                 return false;
@@ -575,69 +588,6 @@
                     e.getMessage());
             return false;
         }
-        return true;
-    }
-
-    /**
-     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
-     * {@code SigningDetails} with multiple signing certificates.
-     * @param certificate certificate to check for membership
-     * @param signingDetails signing certificates record whose members are to be searched
-     * @return true if {@code certificate} is in {@code signingDetails}
-     */
-    public static boolean signingDetailsHasCertificate(
-            byte[] certificate, PackageParser.SigningDetails signingDetails) {
-        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
-            return false;
-        }
-        Signature signature = new Signature(certificate);
-        if (signingDetails.hasPastSigningCertificates()) {
-            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
-                if (signingDetails.pastSigningCertificates[i].equals(signature)) {
-                    return true;
-                }
-            }
-        } else {
-            // no signing history, just check the current signer
-            if (signingDetails.signatures.length == 1
-                    && signingDetails.signatures[0].equals(signature)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
-     * {@code SigningDetails} with multiple signing certificaes.
-     * @param sha256Certificate certificate to check for membership
-     * @param signingDetails signing certificates record whose members are to be searched
-     * @return true if {@code certificate} is in {@code signingDetails}
-     */
-    public static boolean signingDetailsHasSha256Certificate(
-            byte[] sha256Certificate, PackageParser.SigningDetails signingDetails ) {
-        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
-            return false;
-        }
-        if (signingDetails.hasPastSigningCertificates()) {
-            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
-                byte[] digest = PackageUtils.computeSha256DigestBytes(
-                        signingDetails.pastSigningCertificates[i].toByteArray());
-                if (Arrays.equals(sha256Certificate, digest)) {
-                    return true;
-                }
-            }
-        } else {
-            // no signing history, just check the current signer
-            if (signingDetails.signatures.length == 1) {
-                byte[] digest = PackageUtils.computeSha256DigestBytes(
-                        signingDetails.signatures[0].toByteArray());
-                if (Arrays.equals(sha256Certificate, digest)) {
-                    return true;
-                }
-            }
-        }
-        return false;
     }
 
     /** Returns true if APK Verity is enabled. */
@@ -662,10 +612,11 @@
         final String packageName = pkgSetting.name;
         boolean compatMatch = false;
         if (pkgSetting.signatures.mSigningDetails.signatures != null) {
+
             // Already existing package. Make sure signatures match
-            boolean match = compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
-                    parsedSignatures.signatures)
-                    == PackageManager.SIGNATURE_MATCH;
+            boolean match = parsedSignatures.checkCapability(
+                    pkgSetting.signatures.mSigningDetails,
+                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA);
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
                         parsedSignatures);
@@ -673,8 +624,10 @@
             }
             if (!match && compareRecover) {
                 match = matchSignaturesRecover(
-                        packageName, pkgSetting.signatures.mSigningDetails.signatures,
-                        parsedSignatures.signatures);
+                        packageName,
+                        pkgSetting.signatures.mSigningDetails,
+                        parsedSignatures,
+                        PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA);
             }
 
             if (!match && isApkVerificationForced(disabledPkgSetting)) {
@@ -689,20 +642,35 @@
         }
         // Check for shared user signatures
         if (pkgSetting.sharedUser != null
-                && pkgSetting.sharedUser.signatures.mSigningDetails.signatures != null) {
-            // Already existing package. Make sure signatures match
+                && pkgSetting.sharedUser.signatures.mSigningDetails
+                        != PackageParser.SigningDetails.UNKNOWN) {
+
+            // Already existing package. Make sure signatures match.  In case of signing certificate
+            // rotation, the packages with newer certs need to be ok with being sharedUserId with
+            // the older ones.  We check to see if either the new package is signed by an older cert
+            // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
+            // with being sharedUser with the existing signing cert.
             boolean match =
-                    compareSignatures(
-                            pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
-                            parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
+                    parsedSignatures.checkCapability(
+                            pkgSetting.sharedUser.signatures.mSigningDetails,
+                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+                    || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
+                            parsedSignatures,
+                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(
                         packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
             }
             if (!match && compareRecover) {
-                match = matchSignaturesRecover(packageName,
-                        pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
-                        parsedSignatures.signatures);
+                match =
+                        matchSignaturesRecover(packageName,
+                                pkgSetting.sharedUser.signatures.mSigningDetails,
+                                parsedSignatures,
+                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+                        || matchSignaturesRecover(packageName,
+                                parsedSignatures,
+                                pkgSetting.sharedUser.signatures.mSigningDetails,
+                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
                 compatMatch |= match;
             }
             if (!match) {
@@ -725,7 +693,7 @@
                 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
                 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
         ) {
-            Streams.copy(fileIn, fileOut);
+            FileUtils.copy(fileIn, fileOut);
             Os.chmod(dstFile.getAbsolutePath(), 0644);
             return PackageManager.INSTALL_SUCCEEDED;
         } catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 18356c5..f14a684 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
 import android.service.pm.PackageProto;
@@ -236,6 +237,10 @@
         return signatures.mSigningDetails.signatures;
     }
 
+    public PackageParser.SigningDetails getSigningDetails() {
+        return signatures.mSigningDetails;
+    }
+
     /**
      * Makes a shallow copy of the given package settings.
      *
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 0229a37..95f490e 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -291,6 +291,7 @@
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "<pastSigs> encountered multiple times under the same <sigs> at "
                                     + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
                 }
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 7c6b309..a9f1528 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -315,7 +315,8 @@
      *
      * @param pkg object representing the package to be labeled.
      */
-    public static void assignSeInfoValue(PackageParser.Package pkg) {
+    public static void assignSeInfoValue(PackageParser.Package pkg, boolean isPrivileged,
+            int targetSdkVersion) {
         synchronized (sPolicies) {
             if (!sPolicyRead) {
                 if (DEBUG_POLICY) {
@@ -335,10 +336,11 @@
         if (pkg.applicationInfo.targetSandboxVersion == 2)
             pkg.applicationInfo.seInfo += SANDBOX_V2_STR;
 
-        if (pkg.applicationInfo.isPrivilegedApp())
+        if (isPrivileged) {
             pkg.applicationInfo.seInfo += PRIVILEGED_APP_STR;
+        }
 
-        pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion;
+        pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + targetSdkVersion;
 
         if (DEBUG_POLICY_INSTALL) {
             Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
@@ -482,7 +484,11 @@
         Signature[] certs = mCerts.toArray(new Signature[0]);
         if (pkg.mSigningDetails != SigningDetails.UNKNOWN
                 && !Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) {
-            return null;
+
+            // certs aren't exact match, but the package may have rotated from the known system cert
+            if (certs.length > 1 || !pkg.mSigningDetails.hasCertificate(certs[0])) {
+                return null;
+            }
         }
 
         // Check for inner package name matches given that the
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 92fd904..b53d83b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -88,6 +88,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
@@ -387,7 +388,9 @@
             }
             final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
             final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
-            setQuietModeEnabled(userHandle, false, target);
+            // Call setQuietModeEnabled on bg thread to avoid ANR
+            BackgroundThread.getHandler()
+                    .post(() -> setQuietModeEnabled(userHandle, false, target));
         }
     };
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e2123c2..6308766 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1166,9 +1166,9 @@
         final String systemPackageName = mServiceInternal.getKnownPackageName(
                 PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
         final PackageParser.Package systemPackage = getPackage(systemPackageName);
-        return compareSignatures(systemPackage.mSigningDetails.signatures,
-                pkg.mSigningDetails.signatures)
-                == PackageManager.SIGNATURE_MATCH;
+        return pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
+                || systemPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
+                        PackageParser.SigningDetails.CertCapabilities.PERMISSION);
     }
 
     private void grantDefaultPermissionExceptions(int userId) {
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 cb3b107..08f8bbd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1022,12 +1022,24 @@
                 PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
         final PackageParser.Package systemPackage =
                 mPackageManagerInt.getPackage(systemPackageName);
-        boolean allowed = (PackageManagerServiceUtils.compareSignatures(
-                                bp.getSourceSignatures(), pkg.mSigningDetails.signatures)
-                        == PackageManager.SIGNATURE_MATCH)
-                || (PackageManagerServiceUtils.compareSignatures(
-                systemPackage.mSigningDetails.signatures, pkg.mSigningDetails.signatures)
-                        == PackageManager.SIGNATURE_MATCH);
+
+        // check if the package is allow to use this signature permission.  A package is allowed to
+        // use a signature permission if:
+        //     - it has the same set of signing certificates as the source package
+        //     - or its signing certificate was rotated from the source package's certificate
+        //     - or its signing certificate is a previous signing certificate of the defining
+        //       package, and the defining package still trusts the old certificate for permissions
+        //     - or it shares the above relationships with the system package
+        boolean allowed =
+                pkg.mSigningDetails.hasAncestorOrSelf(
+                        bp.getSourcePackageSetting().getSigningDetails())
+                || bp.getSourcePackageSetting().getSigningDetails().checkCapability(
+                        pkg.mSigningDetails,
+                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)
+                || pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
+                || systemPackage.mSigningDetails.checkCapability(
+                        pkg.mSigningDetails,
+                        PackageParser.SigningDetails.CertCapabilities.PERMISSION);
         if (!allowed && (privilegedPermission || oemPermission)) {
             if (pkg.isSystem()) {
                 // For updated system applications, a privileged/oem permission
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index eed3102..bd4aa1c 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -114,6 +114,7 @@
     private static String METRIC_PM = "shutdown_package_manager";
     private static String METRIC_RADIOS = "shutdown_radios";
     private static String METRIC_RADIO = "shutdown_radio";
+    private static String METRIC_SHUTDOWN_TIME_START = "begin_shutdown";
 
     private final Object mActionDoneSync = new Object();
     private boolean mActionDone;
@@ -415,6 +416,7 @@
     public void run() {
         TimingsTraceLog shutdownTimingLog = newTimingsLog();
         shutdownTimingLog.traceBegin("SystemServerShutdown");
+        metricShutdownStart();
         metricStarted(METRIC_SYSTEM_SERVER);
 
         BroadcastReceiver br = new BroadcastReceiver() {
@@ -530,7 +532,7 @@
 
         shutdownTimingLog.traceEnd(); // SystemServerShutdown
         metricEnded(METRIC_SYSTEM_SERVER);
-        saveMetrics(mReboot);
+        saveMetrics(mReboot, mReason);
         // Remaining work will be done by init, including vold shutdown
         rebootOrShutdown(mContext, mReboot, mReason);
     }
@@ -552,6 +554,12 @@
         }
     }
 
+    private static void metricShutdownStart() {
+        synchronized (TRON_METRICS) {
+            TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis());
+        }
+    }
+
     private void setRebootProgress(final int progress, final CharSequence message) {
         mHandler.post(new Runnable() {
             @Override
@@ -673,10 +681,11 @@
         PowerManagerService.lowLevelShutdown(reason);
     }
 
-    private static void saveMetrics(boolean reboot) {
+    private static void saveMetrics(boolean reboot, String reason) {
         StringBuilder metricValue = new StringBuilder();
         metricValue.append("reboot:");
         metricValue.append(reboot ? "y" : "n");
+        metricValue.append(",").append("reason:").append(reason);
         final int metricsSize = TRON_METRICS.size();
         for (int i = 0; i < metricsSize; i++) {
             final String name = TRON_METRICS.keyAt(i);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index df4f8ec..b0b07ea 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.power.BatterySaverPolicy;
@@ -31,6 +32,8 @@
 /**
  * This class keeps track of battery drain rate.
  *
+ * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it.
+ *
  * Test:
  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
  */
@@ -95,8 +98,12 @@
         public int startBatteryLevel;
         public int endBatteryLevel;
 
+        public int startBatteryPercent;
+        public int endBatteryPercent;
+
         public long totalTimeMillis;
         public int totalBatteryDrain;
+        public int totalBatteryDrainPercent;
 
         public long totalMinutes() {
             return totalTimeMillis / 60_000;
@@ -109,16 +116,35 @@
             return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
         }
 
+        public double drainPercentPerHour() {
+            if (totalTimeMillis == 0) {
+                return 0;
+            }
+            return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000));
+        }
+
         @VisibleForTesting
         String toStringForTest() {
             return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
-                    + String.format("%.2f", drainPerHour()) + "}";
+                    + String.format("%.2f", drainPerHour()) + "uA/H,"
+                    + String.format("%.2f", drainPercentPerHour()) + "%"
+                    + "}";
         }
     }
 
+    @VisibleForTesting
+    static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
+
+    @VisibleForTesting
+    static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
+
+    @VisibleForTesting
+    static final String COUNTER_TIME_SECONDS_PREFIX = "battery_saver_stats_seconds_";
+
     private static BatterySavingStats sInstance;
 
     private BatteryManagerInternal mBatteryManagerInternal;
+    private final MetricsLogger mMetricsLogger;
 
     private static final int STATE_NOT_INITIALIZED = -1;
     private static final int STATE_CHARGING = -2;
@@ -136,17 +162,21 @@
     @GuardedBy("mLock")
     final ArrayMap<Integer, Stat> mStats = new ArrayMap<>();
 
+    private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper();
+
     /**
      * Don't call it directly -- use {@link #getInstance()}. Not private for testing.
+     * @param metricsLogger
      */
     @VisibleForTesting
-    BatterySavingStats() {
+    BatterySavingStats(MetricsLogger metricsLogger) {
         mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+        mMetricsLogger = metricsLogger;
     }
 
     public static synchronized BatterySavingStats getInstance() {
         if (sInstance == null) {
-            sInstance = new BatterySavingStats();
+            sInstance = new BatterySavingStats(new MetricsLogger());
         }
         return sInstance;
     }
@@ -154,6 +184,9 @@
     private BatteryManagerInternal getBatteryManagerInternal() {
         if (mBatteryManagerInternal == null) {
             mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+            if (mBatteryManagerInternal == null) {
+                Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+            }
         }
         return mBatteryManagerInternal;
     }
@@ -208,34 +241,38 @@
         return getStat(statesToIndex(batterySaverState, interactiveState, dozeState));
     }
 
+    @VisibleForTesting
     long injectCurrentTime() {
         return SystemClock.elapsedRealtime();
     }
 
+    @VisibleForTesting
     int injectBatteryLevel() {
         final BatteryManagerInternal bmi = getBatteryManagerInternal();
         if (bmi == null) {
-            Slog.wtf(TAG, "BatteryManagerInternal not initialized");
             return 0;
         }
         return bmi.getBatteryChargeCounter();
     }
 
+    @VisibleForTesting
+    int injectBatteryPercent() {
+        final BatteryManagerInternal bmi = getBatteryManagerInternal();
+        if (bmi == null) {
+            return 0;
+        }
+        return bmi.getBatteryLevel();
+    }
+
     /**
      * Called from the outside whenever any of the states changes, when the device is not plugged
      * in.
      */
     public void transitionState(int batterySaverState, int interactiveState, int dozeState) {
         synchronized (mLock) {
-
             final int newState = statesToIndex(
                     batterySaverState, interactiveState, dozeState);
-            if (mCurrentState == newState) {
-                return;
-            }
-
-            endLastStateLocked();
-            startNewStateLocked(newState);
+            transitionStateLocked(newState);
         }
     }
 
@@ -244,36 +281,49 @@
      */
     public void startCharging() {
         synchronized (mLock) {
-            if (mCurrentState < 0) {
-                return;
-            }
-
-            endLastStateLocked();
-            startNewStateLocked(STATE_CHARGING);
+            transitionStateLocked(STATE_CHARGING);
         }
     }
 
-    private void endLastStateLocked() {
+    private void transitionStateLocked(int newState) {
+        if (mCurrentState == newState) {
+            return;
+        }
+        final long now = injectCurrentTime();
+        final int batteryLevel = injectBatteryLevel();
+        final int batteryPercent = injectBatteryPercent();
+
+        endLastStateLocked(now, batteryLevel, batteryPercent);
+        startNewStateLocked(newState, now, batteryLevel, batteryPercent);
+        mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
+    }
+
+    private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
         if (mCurrentState < 0) {
             return;
         }
         final Stat stat = getStat(mCurrentState);
 
-        stat.endBatteryLevel = injectBatteryLevel();
-        stat.endTime = injectCurrentTime();
+        stat.endBatteryLevel = batteryLevel;
+        stat.endBatteryPercent = batteryPercent;
+        stat.endTime = now;
 
         final long deltaTime = stat.endTime - stat.startTime;
         final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+        final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent;
 
         stat.totalTimeMillis += deltaTime;
         stat.totalBatteryDrain += deltaDrain;
+        stat.totalBatteryDrainPercent += deltaPercent;
 
         if (DEBUG) {
             Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
                     + ": " + (deltaTime / 1_000) + "s "
                     + "Start level: " + stat.startBatteryLevel + "uA "
                     + "End level: " + stat.endBatteryLevel + "uA "
-                    + deltaDrain + "uA");
+                    + "Start percent: " + stat.startBatteryPercent + "% "
+                    + "End percent: " + stat.endBatteryPercent + "% "
+                    + "Drain " + deltaDrain + "uA");
         }
         EventLogTags.writeBatterySavingStats(
                 BatterySaverState.fromIndex(mCurrentState),
@@ -281,11 +331,14 @@
                 DozeState.fromIndex(mCurrentState),
                 deltaTime,
                 deltaDrain,
+                deltaPercent,
                 stat.totalTimeMillis,
-                stat.totalBatteryDrain);
+                stat.totalBatteryDrain,
+                stat.totalBatteryDrainPercent);
+
     }
 
-    private void startNewStateLocked(int newState) {
+    private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
         if (DEBUG) {
             Slog.d(TAG, "New state: " + stateToString(newState));
         }
@@ -296,8 +349,9 @@
         }
 
         final Stat stat = getStat(mCurrentState);
-        stat.startBatteryLevel = injectBatteryLevel();
-        stat.startTime = injectCurrentTime();
+        stat.startBatteryLevel = batteryLevel;
+        stat.startBatteryPercent = batteryPercent;
+        stat.startTime = now;
         stat.endTime = 0;
     }
 
@@ -309,7 +363,7 @@
             indent = indent + "  ";
 
             pw.print(indent);
-            pw.println("Battery Saver:       Off                                 On");
+            pw.println("Battery Saver:       Off                                        On");
             dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
                     DozeState.NOT_DOZING, "NonDoze");
             dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, "   Intr",
@@ -341,13 +395,64 @@
         final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
         final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
 
-        pw.println(String.format("%6dm %6dmA %8.1fmA/h      %6dm %6dmA %8.1fmA/h",
+        pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h      %6dm %6dmA (%3d%%) %8.1fmA/h",
                 offStat.totalMinutes(),
                 offStat.totalBatteryDrain / 1000,
+                offStat.totalBatteryDrainPercent,
                 offStat.drainPerHour() / 1000.0,
                 onStat.totalMinutes(),
                 onStat.totalBatteryDrain / 1000,
+                onStat.totalBatteryDrainPercent,
                 onStat.drainPerHour() / 1000.0));
     }
-}
 
+    @VisibleForTesting
+    class MetricsLoggerHelper {
+        private int mLastState = STATE_NOT_INITIALIZED;
+        private long mStartTime;
+        private int mStartBatteryLevel;
+        private int mStartPercent;
+
+        private static final int STATE_CHANGE_DETECT_MASK =
+                (BatterySaverState.MASK << BatterySaverState.SHIFT) |
+                (InteractiveState.MASK << InteractiveState.SHIFT);
+
+        public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) {
+            final boolean stateChanging =
+                    ((mLastState >= 0) ^ (newState >= 0)) ||
+                    (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
+            if (stateChanging) {
+                if (mLastState >= 0) {
+                    final long deltaTime = now - mStartTime;
+                    final int deltaBattery = mStartBatteryLevel - batteryLevel;
+                    final int deltaPercent = mStartPercent - batteryPercent;
+
+                    report(mLastState, deltaTime, deltaBattery, deltaPercent);
+                }
+                mStartTime = now;
+                mStartBatteryLevel = batteryLevel;
+                mStartPercent = batteryPercent;
+            }
+            mLastState = newState;
+        }
+
+        String getCounterSuffix(int state) {
+            final boolean batterySaver =
+                    BatterySaverState.fromIndex(state) != BatterySaverState.OFF;
+            final boolean interactive =
+                    InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE;
+            if (batterySaver) {
+                return interactive ? "11" : "10";
+            } else {
+                return interactive ? "01" : "00";
+            }
+        }
+
+        void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
+            final String suffix = getCounterSuffix(state);
+            mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+            mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
+            mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 192fd63..8da16d7 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -14,12 +14,15 @@
 
 package com.android.server.slice;
 
+import static android.app.slice.SliceManager.PERMISSION_GRANTED;
+
 import android.app.slice.ISliceListener;
 import android.app.slice.Slice;
 import android.app.slice.SliceProvider;
 import android.app.slice.SliceSpec;
 import android.content.ContentProviderClient;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -51,18 +54,16 @@
     @GuardedBy("mLock")
     private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
     @GuardedBy("mLock")
-    private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>();
+    private final ArrayMap<IBinder, ListenerInfo> mListeners = new ArrayMap<>();
     @GuardedBy("mLock")
     private SliceSpec[] mSupportedSpecs = null;
-    @GuardedBy("mLock")
-    private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();
 
     private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;
+    private boolean mSlicePinned;
 
     public PinnedSliceState(SliceManagerService service, Uri uri) {
         mService = service;
         mUri = uri;
-        mService.getHandler().post(this::handleSendPinned);
         mLock = mService.getLock();
     }
 
@@ -102,14 +103,27 @@
     }
 
     public void destroy() {
-        mService.getHandler().post(this::handleSendUnpinned);
+        setSlicePinned(false);
     }
 
     public void onChange() {
         mService.getHandler().post(this::handleBind);
     }
 
-    public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
+    private void setSlicePinned(boolean pinned) {
+        synchronized (mLock) {
+            if (mSlicePinned == pinned) return;
+            mSlicePinned = pinned;
+            if (pinned) {
+                mService.getHandler().post(this::handleSendPinned);
+            } else {
+                mService.getHandler().post(this::handleSendUnpinned);
+            }
+        }
+    }
+
+    public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs,
+            boolean hasPermission) {
         synchronized (mLock) {
             if (mListeners.size() == 0) {
                 mService.listen(mUri);
@@ -118,26 +132,27 @@
                 listener.asBinder().linkToDeath(mDeathRecipient, 0);
             } catch (RemoteException e) {
             }
-            mListeners.put(listener.asBinder(), listener);
-            mPkgMap.put(listener.asBinder(), pkg);
+            mListeners.put(listener.asBinder(), new ListenerInfo(listener, pkg, hasPermission,
+                    Binder.getCallingUid(), Binder.getCallingPid()));
             mergeSpecs(specs);
+            setSlicePinned(hasPermission);
         }
     }
 
     public boolean removeSliceListener(ISliceListener listener) {
         synchronized (mLock) {
             listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
-            mPkgMap.remove(listener.asBinder());
             if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
                 mService.unlisten(mUri);
             }
             mListeners.remove(listener.asBinder());
         }
-        return !isPinned();
+        return !hasPinOrListener();
     }
 
     public void pin(String pkg, SliceSpec[] specs) {
         synchronized (mLock) {
+            setSlicePinned(true);
             mPinnedPkgs.add(pkg);
             mergeSpecs(specs);
         }
@@ -147,7 +162,7 @@
         synchronized (mLock) {
             mPinnedPkgs.remove(pkg);
         }
-        return !isPinned();
+        return !hasPinOrListener();
     }
 
     public boolean isListening() {
@@ -156,8 +171,32 @@
         }
     }
 
+    public void recheckPackage(String pkg) {
+        synchronized (mLock) {
+            for (int i = 0; i < mListeners.size(); i++) {
+                ListenerInfo info = mListeners.valueAt(i);
+                if (!info.hasPermission && Objects.equals(info.pkg, pkg)) {
+                    mService.getHandler().post(() -> {
+                        // This bind lets the app itself participate in the permission grant.
+                        Slice s = doBind(info);
+                        if (mService.checkAccess(info.pkg, mUri, info.callingUid, info.callingPid)
+                                == PERMISSION_GRANTED) {
+                            info.hasPermission = true;
+                            setSlicePinned(true);
+                            try {
+                                info.listener.onSliceUpdated(s);
+                            } catch (RemoteException e) {
+                                checkSelfRemove();
+                            }
+                        }
+                    });
+                }
+            }
+        }
+    }
+
     @VisibleForTesting
-    public boolean isPinned() {
+    public boolean hasPinOrListener() {
         synchronized (mLock) {
             return !mPinnedPkgs.isEmpty() || !mListeners.isEmpty();
         }
@@ -171,61 +210,66 @@
         return client;
     }
 
+    private void checkSelfRemove() {
+        if (!hasPinOrListener()) {
+            // All the listeners died, remove from pinned state.
+            mService.unlisten(mUri);
+            mService.removePinnedSlice(mUri);
+        }
+    }
+
     private void handleRecheckListeners() {
-        if (!isPinned()) return;
+        if (!hasPinOrListener()) return;
         synchronized (mLock) {
             for (int i = mListeners.size() - 1; i >= 0; i--) {
-                ISliceListener l = mListeners.valueAt(i);
-                if (!l.asBinder().isBinderAlive()) {
+                ListenerInfo l = mListeners.valueAt(i);
+                if (!l.listener.asBinder().isBinderAlive()) {
                     mListeners.removeAt(i);
                 }
             }
-            if (!isPinned()) {
-                // All the listeners died, remove from pinned state.
-                mService.unlisten(mUri);
-                mService.removePinnedSlice(mUri);
-            }
+            checkSelfRemove();
         }
     }
 
     private void handleBind() {
         Slice cachedSlice = doBind(null);
         synchronized (mLock) {
-            if (!isPinned()) return;
+            if (!hasPinOrListener()) return;
             for (int i = mListeners.size() - 1; i >= 0; i--) {
-                ISliceListener l = mListeners.valueAt(i);
+                ListenerInfo info = mListeners.valueAt(i);
                 Slice s = cachedSlice;
-                if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
-                    s = doBind(mPkgMap.get(l));
+                if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)
+                        || !info.hasPermission) {
+                    s = doBind(info);
                 }
                 if (s == null) {
                     mListeners.removeAt(i);
                     continue;
                 }
                 try {
-                    l.onSliceUpdated(s);
+                    info.listener.onSliceUpdated(s);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Unable to notify slice " + mUri, e);
                     mListeners.removeAt(i);
                     continue;
                 }
             }
-            if (!isPinned()) {
-                // All the listeners died, remove from pinned state.
-                mService.unlisten(mUri);
-                mService.removePinnedSlice(mUri);
-            }
+            checkSelfRemove();
         }
     }
 
-    private Slice doBind(String overridePkg) {
+    private Slice doBind(ListenerInfo info) {
         try (ContentProviderClient client = getClient()) {
             if (client == null) return null;
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(Arrays.asList(mSupportedSpecs)));
-            extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, overridePkg);
+            if (info != null) {
+                extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, info.pkg);
+                extras.putInt(SliceProvider.EXTRA_OVERRIDE_UID, info.callingUid);
+                extras.putInt(SliceProvider.EXTRA_OVERRIDE_PID, info.callingPid);
+            }
             final Bundle res;
             try {
                 res = client.call(SliceProvider.METHOD_SLICE, null, extras);
@@ -236,6 +280,10 @@
             if (res == null) return null;
             Bundle.setDefusable(res, true);
             return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (Throwable t) {
+            // Calling out of the system process, make sure they don't throw anything at us.
+            Log.e(TAG, "Caught throwable while binding " + mUri, t);
+            return null;
         }
     }
 
@@ -264,4 +312,22 @@
             }
         }
     }
+
+    private class ListenerInfo {
+
+        private ISliceListener listener;
+        private String pkg;
+        private boolean hasPermission;
+        private int callingUid;
+        private int callingPid;
+
+        public ListenerInfo(ISliceListener listener, String pkg, boolean hasPermission,
+                int callingUid, int callingPid) {
+            this.listener = listener;
+            this.pkg = pkg;
+            this.hasPermission = hasPermission;
+            this.callingUid = callingUid;
+            this.callingPid = callingPid;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
new file mode 100644
index 0000000..5e0cd03
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.List;
+
+public class SliceFullAccessList {
+
+    static final int DB_VERSION = 1;
+    private static final String TAG = "SliceFullAccessList";
+
+    private static final String TAG_LIST = "slice-access-list";
+    private static final String TAG_PKG = "pkg";
+    private static final String TAG_USER = "user";
+
+    private final String ATT_USER_ID = "user";
+    private final String ATT_VERSION = "version";
+
+    private final SparseArray<ArraySet<String>> mFullAccessPkgs = new SparseArray<>();
+    private final Context mContext;
+
+    public SliceFullAccessList(Context context) {
+        mContext = context;
+    }
+
+    public boolean hasFullAccess(String pkg, int userId) {
+        ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+        return pkgs != null && pkgs.contains(pkg);
+    }
+
+    public void grantFullAccess(String pkg, int userId) {
+        ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+        if (pkgs == null) {
+            pkgs = new ArraySet<>();
+            mFullAccessPkgs.put(userId, pkgs);
+        }
+        pkgs.add(pkg);
+    }
+
+    public void removeGrant(String pkg, int userId) {
+        ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+        if (pkgs == null) {
+            pkgs = new ArraySet<>();
+            mFullAccessPkgs.put(userId, pkgs);
+        }
+        pkgs.remove(pkg);
+    }
+
+    public void writeXml(XmlSerializer out) throws IOException {
+        out.startTag(null, TAG_LIST);
+        out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
+        final int N = mFullAccessPkgs.size();
+        for (int i = 0 ; i < N; i++) {
+            final int userId = mFullAccessPkgs.keyAt(i);
+            final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
+            out.startTag(null, TAG_USER);
+            out.attribute(null, ATT_USER_ID, Integer.toString(userId));
+            if (pkgs != null) {
+                final int M = pkgs.size();
+                for (int j = 0; j < M; j++) {
+                        out.startTag(null, TAG_PKG);
+                        out.text(pkgs.valueAt(j));
+                        out.endTag(null, TAG_PKG);
+
+                }
+            }
+            out.endTag(null, TAG_USER);
+        }
+        out.endTag(null, TAG_LIST);
+    }
+
+    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+        // upgrade xml
+        int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+        final List<UserInfo> activeUsers = UserManager.get(mContext).getUsers(true);
+        for (UserInfo userInfo : activeUsers) {
+            upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
+        }
+
+        mFullAccessPkgs.clear();
+        // read grants
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            String tag = parser.getName();
+            if (type == XmlPullParser.END_TAG
+                    && TAG_LIST.equals(tag)) {
+                break;
+            }
+            if (type == XmlPullParser.START_TAG) {
+                if (TAG_USER.equals(tag)) {
+                    final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
+                    ArraySet<String> pkgs = new ArraySet<>();
+                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                        String userTag = parser.getName();
+                        if (type == XmlPullParser.END_TAG
+                                && TAG_USER.equals(userTag)) {
+                            break;
+                        }
+                        if (type == XmlPullParser.START_TAG) {
+                            if (TAG_PKG.equals(userTag)) {
+                                final String pkg = parser.nextText();
+                                pkgs.add(pkg);
+                            }
+                        }
+                    }
+                    mFullAccessPkgs.put(userId, pkgs);
+                }
+            }
+        }
+    }
+
+    protected void upgradeXml(final int xmlVersion, final int userId) {}
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c191580..c4871df 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -19,6 +19,8 @@
 import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.Manifest.permission;
 import android.app.ActivityManager;
@@ -29,23 +31,29 @@
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceManager;
 import android.app.slice.SliceSpec;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.Log;
+import android.util.Slog;
+import android.util.Xml.Encoding;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +63,16 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -75,6 +93,9 @@
     private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
     private final Handler mHandler;
     private final ContentObserver mObserver;
+    private final AtomicFile mSliceAccessFile;
+    @GuardedBy("mAccessList")
+    private final SliceFullAccessList mAccessList;
 
     public SliceManagerService(Context context) {
         this(context, createHandler().getLooper());
@@ -99,6 +120,29 @@
                 }
             }
         };
+        final File systemDir = new File(Environment.getDataDirectory(), "system");
+        mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
+        mAccessList = new SliceFullAccessList(mContext);
+
+        synchronized (mSliceAccessFile) {
+            if (!mSliceAccessFile.exists()) return;
+            try {
+                InputStream input = mSliceAccessFile.openRead();
+                XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+                parser.setInput(input, Encoding.UTF_8.name());
+                synchronized (mAccessList) {
+                    mAccessList.readXml(parser);
+                }
+            } catch (IOException | XmlPullParserException e) {
+                Slog.d(TAG, "Can't read slice access file", e);
+            }
+        }
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
 
     ///  ----- Lifecycle stuff -----
@@ -120,8 +164,10 @@
             throws RemoteException {
         verifyCaller(pkg);
         uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
-        enforceAccess(pkg, uri);
-        getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs);
+        enforceCrossUser(pkg, uri);
+        getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs,
+                checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingUid())
+                == PERMISSION_GRANTED);
     }
 
     @Override
@@ -129,7 +175,6 @@
             throws RemoteException {
         verifyCaller(pkg);
         uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
-        enforceAccess(pkg, uri);
         if (getPinnedSlice(uri).removeSliceListener(listener)) {
             removePinnedSlice(uri);
         }
@@ -169,14 +214,14 @@
     @Override
     public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-                == PackageManager.PERMISSION_GRANTED) {
+                == PERMISSION_GRANTED) {
             return SliceManager.PERMISSION_GRANTED;
         }
-        if (hasFullSliceAccess(pkg, uid)) {
+        if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
             return SliceManager.PERMISSION_GRANTED;
         }
         synchronized (mLock) {
-            if (mUserGrants.contains(new SliceGrant(uri, pkg))) {
+            if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
                 return SliceManager.PERMISSION_USER_GRANTED;
             }
         }
@@ -189,21 +234,37 @@
         getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
                 "Slice granting requires MANAGE_SLICE_PERMISSIONS");
         if (allSlices) {
-            // TODO: Manage full access grants.
+            synchronized (mAccessList) {
+                mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+            }
+            mHandler.post(mSaveAccessList);
         } else {
             synchronized (mLock) {
-                mUserGrants.add(new SliceGrant(uri, pkg));
+                mUserGrants.add(new SliceGrant(uri, pkg,
+                        Binder.getCallingUserHandle().getIdentifier()));
             }
-            long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.getContentResolver().notifyChange(uri, null);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.getContentResolver().notifyChange(uri, null);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        synchronized (mLock) {
+            for (PinnedSliceState p : mPinnedSlicesByUri.values()) {
+                p.recheckPackage(pkg);
             }
         }
     }
 
     ///  ----- internal code -----
+    private void removeFullAccess(String pkg, int userId) {
+        synchronized (mAccessList) {
+            mAccessList.removeGrant(pkg, userId);
+        }
+        mHandler.post(mSaveAccessList);
+    }
+
     protected void removePinnedSlice(Uri uri) {
         synchronized (mLock) {
             mPinnedSlicesByUri.remove(uri).destroy();
@@ -249,17 +310,13 @@
         return mHandler;
     }
 
-    private void enforceAccess(String pkg, Uri uri) throws RemoteException {
-        int user = Binder.getCallingUserHandle().getIdentifier();
+    protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
+        int user = UserHandle.getUserId(uid);
         // Check for default launcher/assistant.
-        if (!hasFullSliceAccess(pkg, Binder.getCallingUid())) {
-            try {
-                // Also allow things with uri access.
-                getContext().enforceUriPermission(uri, Binder.getCallingPid(),
-                        Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires permission to the Uri");
-            } catch (SecurityException e) {
+        if (!hasFullSliceAccess(pkg, user)) {
+            // Also allow things with uri access.
+            if (getContext().checkUriPermission(uri, pid, uid,
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
                 // Last fallback (if the calling app owns the authority, then it can have access).
                 long ident = Binder.clearCallingIdentity();
                 try {
@@ -268,17 +325,21 @@
                     ContentProviderHolder holder = null;
                     String providerName = getUriWithoutUserId(uri).getAuthority();
                     try {
-                        holder = activityManager.getContentProviderExternal(
-                                providerName, getUserIdFromUri(uri, user), token);
-                        if (holder == null || holder.info == null
-                                || !Objects.equals(holder.info.packageName, pkg)) {
-                            // No more fallbacks, no access.
-                            throw e;
+                        try {
+                            holder = activityManager.getContentProviderExternal(
+                                    providerName, getUserIdFromUri(uri, user), token);
+                            if (holder == null || holder.info == null
+                                    || !Objects.equals(holder.info.packageName, pkg)) {
+                                return PERMISSION_DENIED;
+                            }
+                        } finally {
+                            if (holder != null && holder.provider != null) {
+                                activityManager.removeContentProviderExternal(providerName, token);
+                            }
                         }
-                    } finally {
-                        if (holder != null && holder.provider != null) {
-                            activityManager.removeContentProviderExternal(providerName, token);
-                        }
+                    } catch (RemoteException e) {
+                        // Can't happen.
+                        e.rethrowAsRuntimeException();
                     }
                 } finally {
                     // I know, the double finally seems ugly, but seems safest for the identity.
@@ -286,23 +347,31 @@
                 }
             }
         }
-        // Lastly check for any multi-userness. Any return statements above here will break this
-        // important check.
+        return PERMISSION_GRANTED;
+    }
+
+    private void enforceCrossUser(String pkg, Uri uri) {
+        int user = Binder.getCallingUserHandle().getIdentifier();
         if (getUserIdFromUri(uri, user) != user) {
             getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
                     "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
         }
     }
 
+    private void enforceAccess(String pkg, Uri uri) throws RemoteException {
+        if (checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingPid())
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Access to slice " + uri + " is required");
+        }
+        enforceCrossUser(pkg, uri);
+    }
+
     private void enforceFullAccess(String pkg, String name, Uri uri) {
         int user = Binder.getCallingUserHandle().getIdentifier();
         if (!hasFullSliceAccess(pkg, user)) {
             throw new SecurityException(String.format("Call %s requires full slice access", name));
         }
-        if (getUserIdFromUri(uri, user) != user) {
-            getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
-                    "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
-        }
+        enforceCrossUser(pkg, uri);
     }
 
     private void verifyCaller(String pkg) {
@@ -395,8 +464,9 @@
     }
 
     private boolean isGrantedFullAccess(String pkg, int userId) {
-        // TODO: This will be user granted access, if we allow this through a prompt.
-        return false;
+        synchronized (mAccessList) {
+            return mAccessList.hasFullAccess(pkg, userId);
+        }
     }
 
     private static ServiceThread createHandler() {
@@ -406,6 +476,63 @@
         return handlerThread;
     }
 
+    private final Runnable mSaveAccessList = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mSliceAccessFile) {
+                final FileOutputStream stream;
+                try {
+                    stream = mSliceAccessFile.startWrite();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to save access file", e);
+                    return;
+                }
+
+                try {
+                    XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+                    out.setOutput(stream, Encoding.UTF_8.name());
+                    synchronized (mAccessList) {
+                        mAccessList.writeXml(out);
+                    }
+                    out.flush();
+                    mSliceAccessFile.finishWrite(stream);
+                } catch (IOException | XmlPullParserException e) {
+                    Slog.w(TAG, "Failed to save access file, restoring backup", e);
+                    mSliceAccessFile.failWrite(stream);
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (userId == UserHandle.USER_NULL) {
+                Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+                return;
+            }
+            Uri data = intent.getData();
+            String pkg = data != null ? data.getSchemeSpecificPart() : null;
+            if (pkg == null) {
+                Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+                return;
+            }
+            switch (intent.getAction()) {
+                case Intent.ACTION_PACKAGE_REMOVED:
+                    final boolean replacing =
+                            intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                    if (!replacing) {
+                        removeFullAccess(pkg, userId);
+                    }
+                    break;
+                case Intent.ACTION_PACKAGE_DATA_CLEARED:
+                    removeFullAccess(pkg, userId);
+                    break;
+            }
+        }
+    };
+
     public static class Lifecycle extends SystemService {
         private SliceManagerService mService;
 
@@ -440,10 +567,12 @@
     private class SliceGrant {
         private final Uri mUri;
         private final String mPkg;
+        private final int mUserId;
 
-        public SliceGrant(Uri uri, String pkg) {
+        public SliceGrant(Uri uri, String pkg, int userId) {
             mUri = uri;
             mPkg = pkg;
+            mUserId = userId;
         }
 
         @Override
@@ -455,7 +584,8 @@
         public boolean equals(Object obj) {
             if (!(obj instanceof SliceGrant)) return false;
             SliceGrant other = (SliceGrant) obj;
-            return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg);
+            return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg)
+                    && (other.mUserId == mUserId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 648fa6a..f498cdd 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -161,7 +161,8 @@
 
     @Override
     public void sendBroadcast(String pkg, String cls) {
-        // TODO: Use a pending intent, and enfoceCallingPermission.
+        // TODO: Use a pending intent.
+        enforceCallingPermission();
         mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
                 UserHandle.SYSTEM);
     }
@@ -239,7 +240,7 @@
         }
     }
 
-    public final static class AppUpdateReceiver extends BroadcastReceiver {
+    private final static class AppUpdateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             /**
@@ -284,7 +285,7 @@
         }
     }
 
-    public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
+    private final static class AnomalyAlarmReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -304,7 +305,7 @@
         }
     }
 
-    public final static class PullingAlarmReceiver extends BroadcastReceiver {
+    private final static class PullingAlarmReceiver extends BroadcastReceiver {
       @Override
       public void onReceive(Context context, Intent intent) {
         if (DEBUG)
@@ -325,7 +326,7 @@
       }
     }
 
-    public final static class ShutdownEventReceiver extends BroadcastReceiver {
+    private final static class ShutdownEventReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             /**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index adb368b..7c170ae 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -56,7 +56,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * A note on locking:  We rely on the fact that calls onto mBar are oneway or
  * if they are local, that they just enqueue messages to not deadlock.
@@ -525,6 +524,26 @@
     }
 
     @Override
+    public void showPinningEnterExitToast(boolean entering) throws RemoteException {
+        if (mBar != null) {
+            try {
+                mBar.showPinningEnterExitToast(entering);
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
+    @Override
+    public void showPinningEscapeToast() throws RemoteException {
+        if (mBar != null) {
+            try {
+                mBar.showPinningEscapeToast();
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
+    @Override
     public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
         if (mBar != null) {
             try {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
new file mode 100644
index 0000000..853c7eb
--- /dev/null
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textclassifier;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextLinksCallback;
+import android.service.textclassifier.ITextSelectionCallback;
+import android.service.textclassifier.TextClassifierService;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextSelection;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemService;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A manager for TextClassifier services.
+ * Apps bind to the TextClassificationManagerService for text classification. This service
+ * reroutes calls to it to a {@link TextClassifierService} that it manages.
+ */
+public final class TextClassificationManagerService extends ITextClassifierService.Stub {
+
+    private static final String LOG_TAG = "TextClassificationManagerService";
+
+    // How long after the last interaction with the service we would unbind
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = TimeUnit.MINUTES.toMillis(1);
+
+    public static final class Lifecycle extends SystemService {
+
+        private final TextClassificationManagerService mManagerService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mManagerService = new TextClassificationManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            try {
+                publishBinderService(Context.TEXT_CLASSIFICATION_SERVICE, mManagerService);
+            } catch (Throwable t) {
+                // Starting this service is not critical to the running of this device and should
+                // therefore not crash the device. If it fails, log the error and continue.
+                Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t);
+            }
+        }
+    }
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final Intent mServiceIntent;
+    private final ServiceConnection mConnection;
+    private final Runnable mUnbind;
+    private final Object mLock;
+    @GuardedBy("mLock")
+    private final Queue<PendingRequest> mPendingRequests;
+
+    @GuardedBy("mLock")
+    private ITextClassifierService mService;
+    @GuardedBy("mLock")
+    private boolean mBinding;
+
+    private TextClassificationManagerService(Context context) {
+        mContext = Preconditions.checkNotNull(context);
+        mHandler = new Handler();
+        mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
+                .setComponent(TextClassifierService.getServiceComponentName(mContext));
+        mConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                synchronized (mLock) {
+                    mService = ITextClassifierService.Stub.asInterface(service);
+                    setBindingLocked(false);
+                    handlePendingRequestsLocked();
+                }
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onBindingDied(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                cleanupService();
+            }
+
+            private void cleanupService() {
+                synchronized (mLock) {
+                    mService = null;
+                    setBindingLocked(false);
+                    handlePendingRequestsLocked();
+                }
+            }
+        };
+        mPendingRequests = new LinkedList<>();
+        mUnbind = this::unbind;
+        mLock = new Object();
+    }
+
+    @Override
+    public void onSuggestSelection(
+            CharSequence text, int selectionStartIndex, int selectionEndIndex,
+            TextSelection.Options options, ITextSelectionCallback callback)
+            throws RemoteException {
+        // TODO(b/72481438): All remote calls need to take userId.
+        validateInput(text, selectionStartIndex, selectionEndIndex, callback);
+
+        if (!bind()) {
+            callback.onFailure();
+            return;
+        }
+
+        synchronized (mLock) {
+            if (isBoundLocked()) {
+                mService.onSuggestSelection(
+                        text, selectionStartIndex, selectionEndIndex, options, callback);
+                scheduleUnbindLocked();
+            } else {
+                final Callable<Void> request = () -> {
+                    onSuggestSelection(
+                            text, selectionStartIndex, selectionEndIndex,
+                            options, callback);
+                    return null;
+                };
+                final Callable<Void> onServiceFailure = () -> {
+                    callback.onFailure();
+                    return null;
+                };
+                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+            }
+        }
+    }
+
+    @Override
+    public void onClassifyText(
+            CharSequence text, int startIndex, int endIndex,
+            TextClassification.Options options, ITextClassificationCallback callback)
+            throws RemoteException {
+        validateInput(text, startIndex, endIndex, callback);
+
+        if (!bind()) {
+            callback.onFailure();
+            return;
+        }
+
+        synchronized (mLock) {
+            if (isBoundLocked()) {
+                mService.onClassifyText(text, startIndex, endIndex, options, callback);
+                scheduleUnbindLocked();
+            } else {
+                final Callable<Void> request = () -> {
+                    onClassifyText(text, startIndex, endIndex, options, callback);
+                    return null;
+                };
+                final Callable<Void> onServiceFailure = () -> {
+                    callback.onFailure();
+                    return null;
+                };
+                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+            }
+        }
+    }
+
+    @Override
+    public void onGenerateLinks(
+            CharSequence text, TextLinks.Options options, ITextLinksCallback callback)
+            throws RemoteException {
+        validateInput(text, callback);
+
+        if (!bind()) {
+            callback.onFailure();
+            return;
+        }
+
+        synchronized (mLock) {
+            if (isBoundLocked()) {
+                mService.onGenerateLinks(text, options, callback);
+                scheduleUnbindLocked();
+            } else {
+                final Callable<Void> request = () -> {
+                    onGenerateLinks(text, options, callback);
+                    return null;
+                };
+                final Callable<Void> onServiceFailure = () -> {
+                    callback.onFailure();
+                    return null;
+                };
+                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+            }
+        }
+    }
+
+    /**
+     * @return true if the service is bound or in the process of being bound.
+     *      Returns false otherwise.
+     */
+    private boolean bind() {
+        synchronized (mLock) {
+            if (isBoundLocked() || isBindingLocked()) {
+                return true;
+            }
+
+            // TODO: Handle bind timeout.
+            final boolean willBind;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                Slog.d(LOG_TAG, "Binding to " + mServiceIntent.getComponent());
+                willBind = mContext.bindServiceAsUser(
+                        mServiceIntent, mConnection,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        Binder.getCallingUserHandle());
+                setBindingLocked(willBind);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return willBind;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean isBoundLocked() {
+        return mService != null;
+    }
+
+    @GuardedBy("mLock")
+    private boolean isBindingLocked() {
+        return mBinding;
+    }
+
+    @GuardedBy("mLock")
+    private void setBindingLocked(boolean binding) {
+        mBinding = binding;
+    }
+
+    private void unbind() {
+        synchronized (mLock) {
+            if (!isBoundLocked()) {
+                return;
+            }
+
+            Slog.d(LOG_TAG, "Unbinding from " + mServiceIntent.getComponent());
+            mContext.unbindService(mConnection);
+
+            synchronized (mLock) {
+                mService = null;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void scheduleUnbindLocked() {
+        mHandler.removeCallbacks(mUnbind);
+        mHandler.postDelayed(mUnbind, TIMEOUT_IDLE_BIND_MILLIS);
+    }
+
+    @GuardedBy("mLock")
+    private void enqueueRequestLocked(
+            Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
+        mPendingRequests.add(new PendingRequest(request, onServiceFailure, binder));
+    }
+
+    @GuardedBy("mLock")
+    private void handlePendingRequestsLocked() {
+        // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService.
+        final PendingRequest[] pendingRequests =
+                mPendingRequests.toArray(new PendingRequest[mPendingRequests.size()]);
+        for (PendingRequest pendingRequest : pendingRequests) {
+            if (isBoundLocked()) {
+                pendingRequest.executeLocked();
+            } else {
+                pendingRequest.notifyServiceFailureLocked();
+            }
+        }
+    }
+
+    private final class PendingRequest implements IBinder.DeathRecipient {
+
+        private final Callable<Void> mRequest;
+        private final Callable<Void> mOnServiceFailure;
+        private final IBinder mBinder;
+
+        /**
+         * Initializes a new pending request.
+         *
+         * @param request action to perform when the service is bound
+         * @param onServiceFailure action to perform when the service dies or disconnects
+         * @param binder binder to the process that made this pending request
+         */
+        PendingRequest(
+                @NonNull Callable<Void> request, @NonNull Callable<Void> onServiceFailure,
+                @NonNull IBinder binder) {
+            mRequest = Preconditions.checkNotNull(request);
+            mOnServiceFailure = Preconditions.checkNotNull(onServiceFailure);
+            mBinder = Preconditions.checkNotNull(binder);
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+
+        @GuardedBy("mLock")
+        void executeLocked() {
+            removeLocked();
+            try {
+                mRequest.call();
+            } catch (Exception e) {
+                Slog.d(LOG_TAG, "Error handling pending request: " + e.getMessage());
+            }
+        }
+
+        @GuardedBy("mLock")
+        void notifyServiceFailureLocked() {
+            removeLocked();
+            try {
+                mOnServiceFailure.call();
+            } catch (Exception e) {
+                Slog.d(LOG_TAG, "Error notifying callback of service failure: "
+                        + e.getMessage());
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                // No need to handle this pending request anymore. Remove.
+                removeLocked();
+            }
+        }
+
+        @GuardedBy("mLock")
+        private void removeLocked() {
+            mPendingRequests.remove(this);
+            mBinder.unlinkToDeath(this, 0);
+        }
+    }
+
+    private static void validateInput(
+            CharSequence text, int startIndex, int endIndex, Object callback)
+            throws RemoteException {
+        try {
+            TextClassifier.Utils.validate(text, startIndex, endIndex, true /* allowInMainThread */);
+            Preconditions.checkNotNull(callback);
+        } catch (IllegalArgumentException | NullPointerException e) {
+            throw new RemoteException(e.getMessage());
+        }
+    }
+
+    private static void validateInput(CharSequence text, Object callback) throws RemoteException {
+        try {
+            TextClassifier.Utils.validate(text, true /* allowInMainThread */);
+            Preconditions.checkNotNull(callback);
+        } catch (IllegalArgumentException | NullPointerException e) {
+            throw new RemoteException(e.getMessage());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/updates/SmartSelectionInstallReceiver.java b/services/core/java/com/android/server/updates/SmartSelectionInstallReceiver.java
index 1457366..eff9a9a 100644
--- a/services/core/java/com/android/server/updates/SmartSelectionInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SmartSelectionInstallReceiver.java
@@ -21,8 +21,8 @@
     public SmartSelectionInstallReceiver() {
         super(
             "/data/misc/textclassifier/",
-            "textclassifier.smartselection.model",
-            "metadata/smartselection",
+            "textclassifier.model",
+            "metadata/classification",
             "version");
     }
 
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 3f32079..b00e595 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -72,20 +72,23 @@
     }
 
     /** Cancels the notification */
-    void cancel() {
+    void cancel(boolean deleteChannel) {
         // We can't call into NotificationManager with WM lock held since it might call into AM.
         // So, we post a message to do it later.
-        mService.mH.post(this::onCancelNotification);
+        mService.mH.post(() -> onCancelNotification(deleteChannel));
     }
 
     /** Don't call with the window manager lock held! */
-    private void onCancelNotification() {
+    private void onCancelNotification(boolean deleteChannel) {
         if (!mPosted) {
             // Notification isn't currently posted...
             return;
         }
         mPosted = false;
         mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
+        if (deleteChannel) {
+            mNotificationManager.deleteNotificationChannel(mNotificationTag);
+        }
     }
 
     /** Don't call with the window manager lock held! */
@@ -146,8 +149,12 @@
 
         final String nameChannel =
                 context.getString(R.string.alert_windows_notification_channel_name, appName);
-        final NotificationChannel channel =
-                new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
+
+        NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag);
+        if (channel != null) {
+            return;
+        }
+        channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
         channel.enableLights(false);
         channel.enableVibration(false);
         channel.setBlockableSystem(true);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7674b5e..2512dbd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -662,7 +662,7 @@
             mWallpaperController.updateWallpaperVisibility();
         }
 
-        w.handleWindowMovedIfNeeded(mPendingTransaction);
+        w.handleWindowMovedIfNeeded();
 
         final WindowStateAnimator winAnimator = w.mWinAnimator;
 
@@ -1542,11 +1542,11 @@
      * Callback used to trigger bounds update after configuration change and get ids of stacks whose
      * bounds were updated.
      */
-    void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+    void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
         for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
             final TaskStack stack = mTaskStackContainers.getChildAt(i);
             if (stack.updateBoundsAfterConfigChange()) {
-                changedStackList.add(stack.mStackId);
+                changedStackList.add(stack);
             }
         }
 
@@ -3599,8 +3599,6 @@
     }
 
     private final class AboveAppWindowContainers extends NonAppWindowContainers {
-        private final Dimmer mDimmer = new Dimmer(this);
-        private final Rect mTmpDimBoundsRect = new Rect();
         AboveAppWindowContainers(String name, WindowManagerService service) {
             super(name, service);
         }
@@ -3632,22 +3630,6 @@
                 imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
             }
         }
-
-        @Override
-        Dimmer getDimmer() {
-            return mDimmer;
-        }
-
-        @Override
-        void prepareSurfaces() {
-            mDimmer.resetDimStates();
-            super.prepareSurfaces();
-            getBounds(mTmpDimBoundsRect);
-
-            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
-                scheduleAnimation();
-            }
-        }
     }
 
     /**
@@ -3679,6 +3661,9 @@
         };
 
         private final String mName;
+        private final Dimmer mDimmer = new Dimmer(this);
+        private final Rect mTmpDimBoundsRect = new Rect();
+
         NonAppWindowContainers(String name, WindowManagerService service) {
             super(service);
             mName = name;
@@ -3722,6 +3707,22 @@
         String getName() {
             return mName;
         }
+
+        @Override
+        Dimmer getDimmer() {
+            return mDimmer;
+        }
+
+        @Override
+        void prepareSurfaces() {
+            mDimmer.resetDimStates();
+            super.prepareSurfaces();
+            getBounds(mTmpDimBoundsRect);
+
+            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+                scheduleAnimation();
+            }
+        }
     }
 
     private class NonMagnifiableWindowContainers extends NonAppWindowContainers {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8269a3b..5bc739e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -92,14 +92,21 @@
             onAnimationFinished();
             return;
         }
-        mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
-        try {
-            mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
-                    mFinishedCallback);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to start remote animation", e);
-            onAnimationFinished();
-        }
+
+        // Scale the timeout with the animator scale the controlling app is using.
+        mHandler.postDelayed(mTimeoutRunnable,
+                (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
+
+        final RemoteAnimationTarget[] animations = createAnimations();
+        mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+            try {
+                mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
+                        mFinishedCallback);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to start remote animation", e);
+                onAnimationFinished();
+            }
+        });
     }
 
     private RemoteAnimationTarget[] createAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index deed7f1..8d1a822 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -47,6 +47,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +127,8 @@
     boolean mOrientationChangeComplete = true;
     boolean mWallpaperActionPending = false;
 
-    private final ArrayList<Integer> mChangedStackList = new ArrayList();
+    private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
+    private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
 
     // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
     // instances will be replaced with an instance that writes a binary representation of all
@@ -333,7 +335,8 @@
 
     /**
      * Set new display override config and return array of ids of stacks that were changed during
-     * update. If called for the default display, global configuration will also be updated.
+     * update. If called for the default display, global configuration will also be updated. Stacks
+     * that are marked for deferred removal are excluded from the returned array.
      */
     int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
         final DisplayContent displayContent = getDisplayContent(displayId);
@@ -346,24 +349,42 @@
         if (!configChanged) {
             return null;
         }
+
         displayContent.onOverrideConfigurationChanged(newConfiguration);
 
+        mTmpStackList.clear();
         if (displayId == DEFAULT_DISPLAY) {
             // Override configuration of the default display duplicates global config. In this case
             // we also want to update the global config.
-            return setGlobalConfigurationIfNeeded(newConfiguration);
+            setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
         } else {
-            return updateStackBoundsAfterConfigChange(displayId);
+            updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
         }
+
+        mTmpStackIds.clear();
+        final int stackCount = mTmpStackList.size();
+
+        for (int i = 0; i < stackCount; ++i) {
+            final TaskStack stack = mTmpStackList.get(i);
+
+            // We only include stacks that are not marked for removal as they do not exist outside
+            // of WindowManager at this point.
+            if (!stack.mDeferRemoval) {
+                mTmpStackIds.add(stack.mStackId);
+            }
+        }
+
+        return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
     }
 
-    private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+    private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
+            List<TaskStack> changedStacks) {
         final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
         if (!configChanged) {
-            return null;
+            return;
         }
         onConfigurationChanged(newConfiguration);
-        return updateStackBoundsAfterConfigChange();
+        updateStackBoundsAfterConfigChange(changedStacks);
     }
 
     @Override
@@ -378,26 +399,18 @@
      * Callback used to trigger bounds update after configuration change and get ids of stacks whose
      * bounds were updated.
      */
-    private int[] updateStackBoundsAfterConfigChange() {
-        mChangedStackList.clear();
-
+    private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
         final int numDisplays = mChildren.size();
         for (int i = 0; i < numDisplays; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+            dc.updateStackBoundsAfterConfigChange(changedStacks);
         }
-
-        return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
     }
 
     /** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
-    private int[] updateStackBoundsAfterConfigChange(int displayId) {
-        mChangedStackList.clear();
-
+    private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
         final DisplayContent dc = getDisplayContent(displayId);
-        dc.updateStackBoundsAfterConfigChange(mChangedStackList);
-
-        return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+        dc.updateStackBoundsAfterConfigChange(changedStacks);
     }
 
     private void prepareFreezingTaskBounds() {
@@ -599,6 +612,8 @@
                     "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
         }
 
+        mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+
         final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
 
         // If we are ready to perform an app transition, check through all of the app tokens to be
@@ -903,10 +918,26 @@
     boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
         final WindowManager.LayoutParams attrs = w.mAttrs;
         final int attrFlags = attrs.flags;
+        final boolean onScreen = w.isOnScreen();
         final boolean canBeSeen = w.isDisplayedLw();
         final int privateflags = attrs.privateFlags;
         boolean displayHasContent = false;
 
+        if (DEBUG_KEEP_SCREEN_ON) {
+            Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: " + w
+                + ", w.mHasSurface: " + w.mHasSurface
+                + ", w.isOnScreen(): " + onScreen
+                + ", w.isDisplayedLw(): " + w.isDisplayedLw()
+                + ", w.mAttrs.userActivityTimeout: " + w.mAttrs.userActivityTimeout);
+        }
+        if (w.mHasSurface && onScreen) {
+            if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
+                mUserActivityTimeout = w.mAttrs.userActivityTimeout;
+                if (DEBUG_KEEP_SCREEN_ON) {
+                    Slog.d(TAG, "mUserActivityTimeout set to " + mUserActivityTimeout);
+                }
+            }
+        }
         if (w.mHasSurface && canBeSeen) {
             if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
                 mHoldScreen = w.mSession;
@@ -919,9 +950,6 @@
             if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
                 mScreenBrightness = w.mAttrs.screenBrightness;
             }
-            if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
-                mUserActivityTimeout = w.mAttrs.userActivityTimeout;
-            }
 
             final int type = attrs.type;
             // This function assumes that the contents of the default display are processed first
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 04ae38e..f09a294 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -547,7 +547,7 @@
             if (allowed) {
                 mAlertWindowNotification.post();
             } else {
-                mAlertWindowNotification.cancel();
+                mAlertWindowNotification.cancel(false /* deleteChannel */);
             }
         }
     }
@@ -586,7 +586,7 @@
         if (mAlertWindowNotification == null) {
             return;
         }
-        mAlertWindowNotification.cancel();
+        mAlertWindowNotification.cancel(true /* deleteChannel */);
         mAlertWindowNotification = null;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index cf54b67..a0d1480 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
@@ -53,6 +54,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.RemoteException;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -736,13 +738,22 @@
     }
 
     private void updateSurfaceBounds() {
-        updateSurfaceBounds(getPendingTransaction());
+        updateSurfaceSize(getPendingTransaction());
+        updateSurfacePosition();
         scheduleAnimation();
     }
 
-    void updateSurfaceBounds(SurfaceControl.Transaction transaction) {
-        updateSurfaceSize(transaction);
-        updateSurfacePosition(transaction);
+    /**
+     * Calculate an amount by which to expand the stack bounds in each direction.
+     * Used to make room for shadows in the pinned windowing mode.
+     */
+    int getStackOutset() {
+        if (inPinnedWindowingMode()) {
+            final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+            return mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+                    displayMetrics);
+        }
+        return 0;
     }
 
     private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
@@ -751,8 +762,13 @@
         }
 
         final Rect stackBounds = getBounds();
-        final int width = stackBounds.width();
-        final int height = stackBounds.height();
+        int width = stackBounds.width();
+        int height = stackBounds.height();
+
+        final int outset = getStackOutset();
+        width += 2*outset;
+        height += 2*outset;
+
         if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
             return;
         }
@@ -1753,4 +1769,12 @@
         mDimmer.stopDim(getPendingTransaction());
         scheduleAnimation();
     }
+
+    @Override
+    void getRelativePosition(Point outPos) {
+        super.getRelativePosition(outPos);
+        final int outset = getStackOutset();
+        outPos.x -= outset;
+        outPos.y -= outset;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index cec13ab..b0d42f2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -92,6 +92,7 @@
      * executed and the corresponding transaction is closed and applied.
      */
     private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
+    private boolean mInExecuteAfterPrepareSurfacesRunnables;
 
     WindowAnimator(final WindowManagerService service) {
         mService = service;
@@ -438,7 +439,13 @@
         scheduleAnimation();
     }
 
-    private void executeAfterPrepareSurfacesRunnables() {
+    void executeAfterPrepareSurfacesRunnables() {
+
+        // Don't even think about to start recursing!
+        if (mInExecuteAfterPrepareSurfacesRunnables) {
+            return;
+        }
+        mInExecuteAfterPrepareSurfacesRunnables = true;
 
         // Traverse in order they were added.
         final int size = mAfterPrepareSurfacesRunnables.size();
@@ -446,5 +453,6 @@
             mAfterPrepareSurfacesRunnables.get(i).run();
         }
         mAfterPrepareSurfacesRunnables.clear();
+        mInExecuteAfterPrepareSurfacesRunnables = false;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0c0ce0e..6bd7f22 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -131,7 +131,7 @@
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         super.onConfigurationChanged(newParentConfig);
-        updateSurfacePosition(getPendingTransaction());
+        updateSurfacePosition();
         scheduleAnimation();
     }
 
@@ -1204,7 +1204,7 @@
         }
     }
 
-    void updateSurfacePosition(SurfaceControl.Transaction transaction) {
+    void updateSurfacePosition() {
         if (mSurfaceControl == null) {
             return;
         }
@@ -1214,12 +1214,8 @@
             return;
         }
 
-        transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+        getPendingTransaction().setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
         mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
-
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            mChildren.get(i).updateSurfacePosition(transaction);
-        }
     }
 
     void getRelativePosition(Point outPos) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 066e4e6..d565a6a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,8 +24,6 @@
 import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -125,7 +123,6 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IAssistDataReceiver;
-import android.app.WindowConfiguration;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -2466,6 +2463,7 @@
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
+
             return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 55c982c..a9f2e03 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1758,7 +1758,7 @@
      * listeners and optionally animate it. Simply checking a change of position is not enough,
      * because being move due to dock divider is not a trigger for animation.
      */
-    void handleWindowMovedIfNeeded(Transaction t) {
+    void handleWindowMovedIfNeeded() {
         if (!hasMoved()) {
             return;
         }
@@ -1776,7 +1776,7 @@
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
                 && getWindowConfiguration().hasMovementAnimations()
                 && !mWinAnimator.mLastHidden) {
-            startMoveAnimation(t, left, top);
+            startMoveAnimation(left, top);
         }
 
         //TODO (multidisplay): Accessibility supported only for the default display.
@@ -4360,7 +4360,7 @@
         commitPendingTransaction();
     }
 
-    private void startMoveAnimation(Transaction t, int left, int top) {
+    private void startMoveAnimation(int left, int top) {
         if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
         final Point oldPosition = new Point();
         final Point newPosition = new Point();
@@ -4369,7 +4369,7 @@
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
                 mService.mSurfaceAnimationRunner);
-        startAnimation(t, adapter);
+        startAnimation(getPendingTransaction(), adapter);
     }
 
     private void startAnimation(Transaction t, AnimationAdapter adapter) {
@@ -4519,7 +4519,7 @@
         if (dimmer != null) {
             applyDims(dimmer);
         }
-        updateSurfacePosition(mPendingTransaction);
+        updateSurfacePosition();
 
         mWinAnimator.prepareSurfaceLocked(true);
         super.prepareSurfaces();
@@ -4541,7 +4541,11 @@
     }
 
     @Override
-    void updateSurfacePosition(Transaction t) {
+    void updateSurfacePosition() {
+        updateSurfacePosition(getPendingTransaction());
+    }
+
+    private void updateSurfacePosition(Transaction t) {
         if (mSurfaceControl == null) {
             return;
         }
@@ -4572,6 +4576,17 @@
             outPoint.offset(-parentBounds.left, -parentBounds.top);
         }
 
+        TaskStack stack = getStack();
+
+        // If we have stack outsets, that means the top-left
+        // will be outset, and we need to inset ourselves
+        // to account for it. If we actually have shadows we will
+        // then un-inset ourselves by the surfaceInsets.
+        if (stack != null) {
+            final int outset = stack.getStackOutset();
+            outPoint.offset(outset, outset);
+        }
+
         // Expand for surface insets. See WindowState.expandForSurfaceInsets.
         outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1cfa956..5c9cfbb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -99,7 +99,6 @@
     // Unchanging local convenience fields.
     final WindowManagerService mService;
     final WindowState mWin;
-    private final WindowStateAnimator mParentWinAnimator;
     final WindowAnimator mAnimator;
     final Session mSession;
     final WindowManagerPolicy mPolicy;
@@ -135,8 +134,6 @@
     float mAlpha = 0;
     float mLastAlpha = 0;
 
-    boolean mHasClipRect;
-    Rect mClipRect = new Rect();
     Rect mTmpClipRect = new Rect();
     Rect mTmpFinalClipRect = new Rect();
     Rect mLastClipRect = new Rect();
@@ -150,7 +147,6 @@
      * system decorations.
      */
     private final Rect mSystemDecorRect = new Rect();
-    private final Rect mLastSystemDecorRect = new Rect();
 
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -224,7 +220,6 @@
         mContext = service.mContext;
 
         mWin = win;
-        mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator;
         mSession = win.mSession;
         mAttrType = win.mAttrs.type;
         mIsWallpaper = win.mIsWallpaper;
@@ -455,9 +450,6 @@
         }
 
         // We may abort, so initialize to defaults.
-        mLastSystemDecorRect.set(0, 0, 0, 0);
-        mHasClipRect = false;
-        mClipRect.set(0, 0, 0, 0);
         mLastClipRect.set(0, 0, 0, 0);
 
         // Set up surface control with initial size.
@@ -649,14 +641,12 @@
     }
 
     void computeShownFrameLocked() {
-
         final int displayId = mWin.getDisplayId();
         final ScreenRotationAnimation screenRotationAnimation =
                 mAnimator.getScreenRotationAnimationLocked(displayId);
         final boolean screenAnimation =
                 screenRotationAnimation != null && screenRotationAnimation.isAnimating();
 
-        mHasClipRect = false;
         if (screenAnimation) {
             // cache often used attributes locally
             final Rect frame = mWin.mFrame;
@@ -796,9 +786,9 @@
 
         // We use the clip rect as provided by the tranformation for non-fullscreen windows to
         // avoid premature clipping with the system decor rect.
-        clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : mSystemDecorRect);
+        clipRect.set(mSystemDecorRect);
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
-                + " mHasClipRect=" + mHasClipRect + " fullscreen=" + fullscreen);
+                + " fullscreen=" + fullscreen);
 
         if (isFreeformResizing && !w.isChildWindow()) {
             // For freeform resizing non child windows, we are using the big surface positioned
@@ -808,12 +798,6 @@
 
         w.expandForSurfaceInsets(clipRect);
 
-        if (mHasClipRect && fullscreen) {
-            // We intersect the clip rect specified by the transformation with the expanded system
-            // decor rect to prevent artifacts from drawing during animation if the transformation
-            // clip rect extends outside the system decor rect.
-            clipRect.intersect(mClipRect);
-        }
         // The clip rect was generated assuming (0,0) as the window origin,
         // so we need to translate to match the actual surface coordinates.
         clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top);
@@ -1378,8 +1362,6 @@
             pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
             pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
             pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
-            pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
-            pw.print(" mHasClipRect="); pw.print(mHasClipRect);
             pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
 
             if (!mLastFinalClipRect.isEmpty()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index d1cc5de..9fcf3ee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -19,12 +19,10 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.os.PersistableBundle;
-import android.os.UserHandle;
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.telephony.data.ApnSetting;
 
-import com.android.internal.R;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
@@ -107,11 +105,6 @@
     }
 
     @Override
-    public boolean startUserInBackground(ComponentName who, UserHandle userHandle) {
-        return false;
-    }
-
-    @Override
     public void setStartUserSessionMessage(
             ComponentName admin, CharSequence startUserSessionMessage) {}
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dae7605..4c57f7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.BIND_DEVICE_ADMIN;
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
@@ -68,9 +67,6 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
         .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 
@@ -249,7 +245,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -9030,7 +9025,7 @@
     }
 
     @Override
-    public boolean startUserInBackground(ComponentName who, UserHandle userHandle) {
+    public int startUserInBackground(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(userHandle, "UserHandle is null");
 
@@ -9041,27 +9036,31 @@
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
             Log.w(LOG_TAG, "Managed profile cannot be started in background");
-            return false;
+            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
                 Log.w(LOG_TAG, "Cannot start more users in background");
-                return false;
+                return DevicePolicyManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
             }
 
-            return mInjector.getIActivityManager().startUserInBackground(userId);
+            if (mInjector.getIActivityManager().startUserInBackground(userId)) {
+                return DevicePolicyManager.USER_OPERATION_SUCCESS;
+            } else {
+                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+            }
         } catch (RemoteException e) {
             // Same process, should not happen.
-            return false;
+            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
     @Override
-    public boolean stopUser(ComponentName who, UserHandle userHandle) {
+    public int stopUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(userHandle, "UserHandle is null");
 
@@ -9072,23 +9071,14 @@
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
             Log.w(LOG_TAG, "Managed profile cannot be stopped");
-            return false;
+            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
-        final long id = mInjector.binderClearCallingIdentity();
-        try {
-            return mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)
-                    == USER_OP_SUCCESS;
-        } catch (RemoteException e) {
-            // Same process, should not happen.
-            return false;
-        } finally {
-            mInjector.binderRestoreCallingIdentity(id);
-        }
+        return stopUserUnchecked(userId);
     }
 
     @Override
-    public boolean logoutUser(ComponentName who) {
+    public int logoutUser(ComponentName who) {
         Preconditions.checkNotNull(who, "ComponentName is null");
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
@@ -9102,20 +9092,40 @@
 
         if (isManagedProfile(callingUserId)) {
             Log.w(LOG_TAG, "Managed profile cannot be logout");
-            return false;
+            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
                 Log.w(LOG_TAG, "Failed to switch to primary user");
-                return false;
+                // This should never happen as target user is UserHandle.USER_SYSTEM
+                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
             }
-            return mInjector.getIActivityManager().stopUser(callingUserId, true /*force*/, null)
-                    == USER_OP_SUCCESS;
         } catch (RemoteException e) {
             // Same process, should not happen.
-            return false;
+            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+
+        return stopUserUnchecked(callingUserId);
+    }
+
+    private int stopUserUnchecked(int userId) {
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            switch (mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)) {
+                case ActivityManager.USER_OP_SUCCESS:
+                    return DevicePolicyManager.USER_OPERATION_SUCCESS;
+                case ActivityManager.USER_OP_IS_CURRENT:
+                    return DevicePolicyManager.USER_OPERATION_ERROR_CURRENT_USER;
+                default:
+                    return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+            }
+        } catch (RemoteException e) {
+            // Same process, should not happen.
+            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -11437,16 +11447,11 @@
     }
 
     private Set<String> getMeteredDisabledPackagesLocked(int userId) {
-        final DevicePolicyData policy = getUserData(userId);
+        final ComponentName who = getOwnerComponent(userId);
         final Set<String> restrictedPkgs = new ArraySet<>();
-        for (int i = policy.mAdminList.size() - 1; i >= 0; --i) {
-            final ActiveAdmin admin = policy.mAdminList.get(i);
-            if (!isActiveAdminWithPolicyForUserLocked(admin,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) {
-                // Not a profile or device owner, ignore
-                continue;
-            }
-            if (admin.meteredDisabledPackages != null) {
+        if (who != null) {
+            final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
+            if (admin != null && admin.meteredDisabledPackages != null) {
                 restrictedPkgs.addAll(admin.meteredDisabledPackages);
             }
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f95c6f0..210fd47 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -111,6 +111,7 @@
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
 import com.android.server.telecom.TelecomLoaderService;
+import com.android.server.textclassifier.TextClassificationManagerService;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
@@ -733,6 +734,8 @@
                 false);
         boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices",
                 false);
+        boolean disableSystemTextClassifier = SystemProperties.getBoolean(
+                "config.disable_systemtextclassifier", false);
         boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false);
         boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -1066,6 +1069,12 @@
                 traceEnd();
             }
 
+            if (!disableSystemTextClassifier) {
+                traceBeginAndSlog("StartTextClassificationManagerService");
+                mSystemServiceManager.startService(TextClassificationManagerService.Lifecycle.class);
+                traceEnd();
+            }
+
             traceBeginAndSlog("StartNetworkScoreService");
             try {
                 networkScore = new NetworkScoreService(context);
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
index c397f23..0752537 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
@@ -26,6 +26,7 @@
 
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,7 +37,7 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, sdk = 26)
-@SystemLoaderClasses({BackupManagerConstants.class})
+@SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class BackupManagerConstantsTest {
     private static final String PACKAGE_NAME = "some.package.name";
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index b60ad4b..df09780 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -48,7 +48,8 @@
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
+
 import java.io.File;
 import java.util.HashMap;
 import java.util.List;
@@ -74,11 +75,7 @@
     sdk = 26,
     shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class}
 )
-@SystemLoaderClasses({
-    BackupManagerService.class,
-    TransportManager.class,
-    PackageManagerBackupAgent.class
-})
+@SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class BackupManagerServiceTest {
     private static final String TAG = "BMSTest";
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index 1360828..e103464 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -69,6 +69,7 @@
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
 import com.android.server.testing.shadows.FrameworkShadowPackageManager;
 import com.android.server.testing.shadows.ShadowBackupDataInput;
 import com.android.server.testing.shadows.ShadowBackupDataOutput;
@@ -102,12 +103,10 @@
         ShadowQueuedWork.class
     }
 )
+@SystemLoaderPackages({"com.android.server.backup"})
 @SystemLoaderClasses({
-    BackupManagerService.class,
-    PerformBackupTask.class,
     BackupDataOutput.class,
     FullBackupDataOutput.class,
-    TransportManager.class,
     BackupAgent.class,
     IBackupTransport.class,
     IBackupAgent.class,
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 068fe81..44ac803 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -51,7 +51,7 @@
 import com.android.server.backup.transport.TransportClientManager;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
 import com.android.server.testing.shadows.FrameworkShadowContextImpl;
 import com.android.server.testing.shadows.FrameworkShadowPackageManager;
 import java.util.ArrayList;
@@ -75,7 +75,7 @@
     sdk = 26,
     shadows = {FrameworkShadowPackageManager.class, FrameworkShadowContextImpl.class}
 )
-@SystemLoaderClasses({TransportManager.class})
+@SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class TransportManagerTest {
     private static final String PACKAGE_A = "some.package.a";
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index 55fb460..5810c30 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -49,7 +49,7 @@
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -67,11 +67,7 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, sdk = 26)
-@SystemLoaderClasses({
-    BackupManagerService.class,
-    PerformInitializeTaskTest.class,
-    TransportManager.class
-})
+@SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class PerformInitializeTaskTest {
     @Mock private BackupManagerService mBackupManagerService;
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index db6e62f..ff1644c 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -46,6 +46,7 @@
 import com.android.server.backup.TransportManager;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
 import com.android.server.testing.shadows.ShadowCloseGuard;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowSlog;
@@ -66,7 +67,7 @@
     sdk = 26,
     shadows = {ShadowEventLog.class, ShadowCloseGuard.class, ShadowSlog.class}
 )
-@SystemLoaderClasses({TransportManager.class, TransportClient.class})
+@SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class TransportClientTest {
     private static final String PACKAGE_NAME = "some.package.name";
diff --git a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
index c94d598..d2a4d06 100644
--- a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
+++ b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
@@ -16,8 +16,7 @@
 
 package com.android.server.testing;
 
-import com.android.server.backup.PerformBackupTaskTest;
-import com.android.server.backup.internal.PerformBackupTask;
+import static java.util.Arrays.asList;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -33,10 +32,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.util.Arrays;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Set;
+import java.util.stream.Stream;
 
 import javax.annotation.Nonnull;
 
@@ -51,9 +51,9 @@
  * against the actual classes that are in the tree, not a past version of them. Ideally we would
  * have a locally built jar referenced by Robolectric, but until that happens one can use this
  * class.
- * This class reads the {@link SystemLoaderClasses} annotation on test classes and for each class
- * in that annotation value it will bypass the android jar and load it from the system class loader.
- * Allowing the test to test the actual class in the tree.
+ * This class reads the {@link SystemLoaderClasses} or {@link SystemLoaderPackages} annotations on
+ * test classes, for classes that match the annotations it will bypass the android jar and load it
+ * from the system class loader. Allowing the test to test the actual class in the tree.
  *
  * Implementation note: One could think about overriding
  * {@link RobolectricTestRunner#createClassLoaderConfig(FrameworkMethod)} method and putting the
@@ -72,11 +72,21 @@
 
     public FrameworkRobolectricTestRunner(Class<?> testClass) throws InitializationError {
         super(testClass);
-        SystemLoaderClasses annotation = testClass.getAnnotation(SystemLoaderClasses.class);
-        Class<?>[] systemLoaderClasses =
-                (annotation != null) ? annotation.value() : new Class<?>[0];
-        Set<String> systemLoaderClassNames = classesToClassNames(systemLoaderClasses);
-        mSandboxFactory = new FrameworkSandboxFactory(systemLoaderClassNames);
+        Set<String> classPrefixes = getSystemLoaderClassPrefixes(testClass);
+        mSandboxFactory = new FrameworkSandboxFactory(classPrefixes);
+    }
+
+    private Set<String> getSystemLoaderClassPrefixes(Class<?> testClass) {
+        Set<String> classPrefixes = new HashSet<>();
+        SystemLoaderClasses byClass = testClass.getAnnotation(SystemLoaderClasses.class);
+        if (byClass != null) {
+            Stream.of(byClass.value()).map(Class::getName).forEach(classPrefixes::add);
+        }
+        SystemLoaderPackages byPackage = testClass.getAnnotation(SystemLoaderPackages.class);
+        if (byPackage != null) {
+            classPrefixes.addAll(asList(byPackage.value()));
+        }
+        return classPrefixes;
     }
 
     @Nonnull
@@ -92,15 +102,15 @@
     }
 
     private static class FrameworkClassLoader extends SandboxClassLoader {
-        private final Set<String> mSystemLoaderClasses;
+        private final Set<String> mSystemLoaderClassPrefixes;
 
         private FrameworkClassLoader(
-                Set<String> systemLoaderClasses,
+                Set<String> systemLoaderClassPrefixes,
                 ClassLoader systemClassLoader,
                 InstrumentationConfiguration instrumentationConfig,
                 URL... urls) {
             super(systemClassLoader, instrumentationConfig, urls);
-            mSystemLoaderClasses = systemLoaderClasses;
+            mSystemLoaderClassPrefixes = systemLoaderClassPrefixes;
         }
 
         @Override
@@ -146,8 +156,8 @@
          * loader, so we test if the classes in the annotation are prefixes of the class to load.
          */
         private boolean shouldLoadFromSystemLoader(String className) {
-            for (String classNamePrefix : mSystemLoaderClasses) {
-                if (className.startsWith(classNamePrefix)) {
+            for (String classPrefix : mSystemLoaderClassPrefixes) {
+                if (className.startsWith(classPrefix)) {
                     return true;
                 }
             }
@@ -156,10 +166,10 @@
     }
 
     private static class FrameworkSandboxFactory extends SandboxFactory {
-        private final Set<String> mSystemLoaderClasses;
+        private final Set<String> mSystemLoaderClassPrefixes;
 
-        private FrameworkSandboxFactory(Set<String> systemLoaderClasses) {
-            mSystemLoaderClasses = systemLoaderClasses;
+        private FrameworkSandboxFactory(Set<String> systemLoaderClassPrefixes) {
+            mSystemLoaderClassPrefixes = systemLoaderClassPrefixes;
         }
 
         @Nonnull
@@ -167,18 +177,10 @@
         public ClassLoader createClassLoader(
                 InstrumentationConfiguration instrumentationConfig, URL... urls) {
             return new FrameworkClassLoader(
-                    mSystemLoaderClasses,
+                    mSystemLoaderClassPrefixes,
                     ClassLoader.getSystemClassLoader(),
                     instrumentationConfig,
                     urls);
         }
     }
-
-    private static Set<String> classesToClassNames(Class<?>[] classes) {
-        ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-        for (Class<?> classObject : classes) {
-            builder.add(classObject.getName());
-        }
-        return builder.build();
-    }
 }
diff --git a/services/robotests/src/com/android/server/testing/SystemLoaderPackages.java b/services/robotests/src/com/android/server/testing/SystemLoaderPackages.java
new file mode 100644
index 0000000..e01c0a4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/SystemLoaderPackages.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to be used in test classes that run with {@link FrameworkRobolectricTestRunner}.
+ * This will make the classes under the specified packages be loaded from the system class loader,
+ * NOT from the Robolectric android jar.
+ *
+ * @see FrameworkRobolectricTestRunner
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SystemLoaderPackages {
+    String[] value() default {};
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index d2ae22b..1cbb399 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,7 +90,6 @@
     @Mock private IStatusBarService mStatusBarService;
     @Mock private WindowManagerService mWindowManager;
     @Mock private LockPatternUtils mLockPatternUtils;
-    @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
     @Mock private TelecomManager mTelecomManager;
     @Mock private RecentTasks mRecentTasks;
@@ -123,7 +122,6 @@
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
         mLockTaskController.mTelecomManager = mTelecomManager;
         mLockTaskController.mLockPatternUtils = mLockPatternUtils;
-        mLockTaskController.mLockTaskNotify = mLockTaskNotify;
 
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
@@ -208,7 +206,7 @@
         // THEN lock task mode should be started
         verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
         // THEN screen pinning toast should be shown
-        verify(mLockTaskNotify).showPinningStartToast();
+        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
     }
 
     @Test
@@ -377,7 +375,7 @@
         // THEN the keyguard should be shown
         verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
         // THEN screen pinning toast should be shown
-        verify(mLockTaskNotify).showPinningExitToast();
+        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
new file mode 100644
index 0000000..8502e69
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessStatsTrackerTest {
+
+    private TestInjector mTestInjector;
+
+    @Before
+    public void setUp() {
+        mTestInjector = new TestInjector();
+    }
+
+    @Test
+    public void testBrightnessStatsTrackerOverSingleDay() {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        float[] expectedStats;
+        // Test case where no user data
+        userStats = statsTracker.getUserStats(0);
+        assertNull(userStats);
+        // Test after adding some user data
+        statsTracker.start();
+        statsTracker.add(0, 0);
+        mTestInjector.incrementTime(1000);
+        statsTracker.stop();
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(1, userStats.size());
+        assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 1;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+        // Test after adding some more user data
+        statsTracker.start();
+        statsTracker.add(0, 0.05f);
+        mTestInjector.incrementTime(1000);
+        statsTracker.add(0, 0.2f);
+        mTestInjector.incrementTime(1500);
+        statsTracker.add(0, 50000);
+        mTestInjector.incrementTime(2500);
+        statsTracker.stop();
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(1, userStats.size());
+        assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 2;
+        expectedStats[1] = 1.5f;
+        expectedStats[11] = 2.5f;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+    }
+
+    @Test
+    public void testBrightnessStatsTrackerOverMultipleDays() {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        float[] expectedStats;
+        // Add data for day 1
+        statsTracker.start();
+        statsTracker.add(0, 0.05f);
+        mTestInjector.incrementTime(1000);
+        statsTracker.add(0, 0.2f);
+        mTestInjector.incrementTime(1500);
+        statsTracker.add(0, 1);
+        mTestInjector.incrementTime(2500);
+        statsTracker.stop();
+        // Add data for day 2
+        mTestInjector.incrementDate(1);
+        statsTracker.start();
+        statsTracker.add(0, 0);
+        mTestInjector.incrementTime(3500);
+        statsTracker.add(0, 5);
+        mTestInjector.incrementTime(5000);
+        statsTracker.stop();
+        // Test that the data is tracked as expected
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(2, userStats.size());
+        assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 1;
+        expectedStats[1] = 1.5f;
+        expectedStats[3] = 2.5f;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+        assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 3.5f;
+        expectedStats[4] = 5;
+        assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+    }
+
+    @Test
+    public void testBrightnessStatsTrackerOverMultipleUsers() {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        float[] expectedStats;
+        // Add data for user 1
+        statsTracker.start();
+        statsTracker.add(0, 0.05f);
+        mTestInjector.incrementTime(1000);
+        statsTracker.add(0, 0.2f);
+        mTestInjector.incrementTime(1500);
+        statsTracker.add(0, 1);
+        mTestInjector.incrementTime(2500);
+        statsTracker.stop();
+        // Add data for user 2
+        mTestInjector.incrementDate(1);
+        statsTracker.start();
+        statsTracker.add(1, 0);
+        mTestInjector.incrementTime(3500);
+        statsTracker.add(1, 5);
+        mTestInjector.incrementTime(5000);
+        statsTracker.stop();
+        // Test that the data is tracked as expected
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(1, userStats.size());
+        assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 1;
+        expectedStats[1] = 1.5f;
+        expectedStats[3] = 2.5f;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+        userStats = statsTracker.getUserStats(1);
+        assertEquals(1, userStats.size());
+        assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 3.5f;
+        expectedStats[4] = 5;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+    }
+
+    @Test
+    public void testBrightnessStatsTrackerOverMaxDays() {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        // Add 10 extra days of data over the buffer limit
+        for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) {
+            mTestInjector.incrementDate(1);
+            statsTracker.start();
+            statsTracker.add(0, 10);
+            mTestInjector.incrementTime(1000);
+            statsTracker.add(0, 20);
+            mTestInjector.incrementTime(1000);
+            statsTracker.stop();
+        }
+        // Assert that we are only tracking last "MAX_DAYS_TO_TRACK"
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size());
+        LocalDate runningDate = mTestInjector.getLocalDate();
+        for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) {
+            assertEquals(runningDate, userStats.get(i).getLocalDate());
+            runningDate = runningDate.minusDays(1);
+        }
+    }
+
+    @Test
+    public void testReadAmbientBrightnessStats() throws IOException {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        LocalDate date = mTestInjector.getLocalDate();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        String statsFile =
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+                        + "<ambient-brightness-stats>\r\n"
+                        // Old stats that shouldn't be read
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+                        + date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK)
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+                        + "0.0,0.0,0.0\" />\r\n"
+                        // Valid stats that should get read
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+                        + date.minusDays(1)
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+                        + "0.0,0.0,0.0\" />\r\n"
+                        // Valid stats that should get read
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+                        + "0.0\" />\r\n"
+                        + "</ambient-brightness-stats>";
+        statsTracker.readStats(getInputStream(statsFile));
+        userStats = statsTracker.getUserStats(0);
+        assertEquals(2, userStats.size());
+        assertEquals(new AmbientBrightnessDayStats(date.minusDays(1),
+                new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+                new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0));
+        assertEquals(new AmbientBrightnessDayStats(date,
+                new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+                new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1));
+    }
+
+    @Test
+    public void testFailedReadAmbientBrightnessStatsWithException() {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        LocalDate date = mTestInjector.getLocalDate();
+        String statsFile;
+        // Test with parse error
+        statsFile =
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+                        + "<ambient-brightness-stats>\r\n"
+                        // Incorrect since bucket boundaries not parsable
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+                        + "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+                        + "0.0,0.0,0.0\" />\r\n"
+                        + "</ambient-brightness-stats>";
+        try {
+            statsTracker.readStats(getInputStream(statsFile));
+        } catch (IOException e) {
+            // Expected
+        }
+        assertNull(statsTracker.getUserStats(0));
+        // Test with incorrect data (bucket boundaries length not equal to stats length)
+        statsFile =
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+                        + "<ambient-brightness-stats>\r\n"
+                        // Correct data
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+                        + date.minusDays(1)
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+                        + "0.0\" />\r\n"
+                        // Incorrect data
+                        + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0,"
+                        + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+                        + "0.0,0.0,0.0\" />\r\n"
+                        + "</ambient-brightness-stats>";
+        try {
+            statsTracker.readStats(getInputStream(statsFile));
+        } catch (Exception e) {
+            // Expected
+        }
+        assertNull(statsTracker.getUserStats(0));
+        // Test with missing attribute
+        statsFile =
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+                        + "<ambient-brightness-stats>\r\n"
+                        + "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date
+                        + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+                        + "3000.0,10000.0\" />\r\n"
+                        + "</ambient-brightness-stats>";
+        try {
+            statsTracker.readStats(getInputStream(statsFile));
+        } catch (Exception e) {
+            // Expected
+        }
+        assertNull(statsTracker.getUserStats(0));
+    }
+
+    @Test
+    public void testWriteThenReadAmbientBrightnessStats() throws IOException {
+        AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+        ArrayList<AmbientBrightnessDayStats> userStats;
+        float[] expectedStats;
+        // Generate some dummy data
+        // Data: very old which should not be read
+        statsTracker.start();
+        statsTracker.add(0, 0.05f);
+        mTestInjector.incrementTime(1000);
+        statsTracker.add(0, 0.2f);
+        mTestInjector.incrementTime(1500);
+        statsTracker.add(0, 1);
+        mTestInjector.incrementTime(2500);
+        statsTracker.stop();
+        // Data: day 1 user 1
+        mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1);
+        statsTracker.start();
+        statsTracker.add(0, 0.05f);
+        mTestInjector.incrementTime(1000);
+        statsTracker.add(0, 0.2f);
+        mTestInjector.incrementTime(1500);
+        statsTracker.add(0, 1);
+        mTestInjector.incrementTime(2500);
+        statsTracker.stop();
+        // Data: day 1 user 2
+        statsTracker.start();
+        statsTracker.add(1, 0);
+        mTestInjector.incrementTime(3500);
+        statsTracker.add(1, 5);
+        mTestInjector.incrementTime(5000);
+        statsTracker.stop();
+        // Data: day 2 user 1
+        mTestInjector.incrementDate(1);
+        statsTracker.start();
+        statsTracker.add(0, 0);
+        mTestInjector.incrementTime(3500);
+        statsTracker.add(0, 50000);
+        mTestInjector.incrementTime(5000);
+        statsTracker.stop();
+        // Write them
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        statsTracker.writeStats(baos);
+        baos.flush();
+        // Read them back and assert that it's the same
+        ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
+        AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker();
+        newStatsTracker.readStats(input);
+        userStats = newStatsTracker.getUserStats(0);
+        assertEquals(2, userStats.size());
+        // Check day 1 user 1
+        assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 1;
+        expectedStats[1] = 1.5f;
+        expectedStats[3] = 2.5f;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+        // Check day 2 user 1
+        assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 3.5f;
+        expectedStats[11] = 5;
+        assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+        userStats = newStatsTracker.getUserStats(1);
+        assertEquals(1, userStats.size());
+        // Check day 1 user 2
+        assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+        expectedStats = getEmptyStatsArray();
+        expectedStats[0] = 3.5f;
+        expectedStats[4] = 5;
+        assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+    }
+
+    @Test
+    public void testTimer() {
+        AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer(
+                () -> mTestInjector.elapsedRealtimeMillis());
+        assertEquals(0, timer.totalDurationSec(), 0);
+        mTestInjector.incrementTime(1000);
+        assertEquals(0, timer.totalDurationSec(), 0);
+        assertFalse(timer.isRunning());
+        // Start timer
+        timer.start();
+        assertTrue(timer.isRunning());
+        assertEquals(0, timer.totalDurationSec(), 0);
+        mTestInjector.incrementTime(1000);
+        assertTrue(timer.isRunning());
+        assertEquals(1, timer.totalDurationSec(), 0);
+        // Reset timer
+        timer.reset();
+        assertEquals(0, timer.totalDurationSec(), 0);
+        assertFalse(timer.isRunning());
+        // Start again
+        timer.start();
+        assertTrue(timer.isRunning());
+        assertEquals(0, timer.totalDurationSec(), 0);
+        mTestInjector.incrementTime(2000);
+        assertTrue(timer.isRunning());
+        assertEquals(2, timer.totalDurationSec(), 0);
+        // Reset again
+        timer.reset();
+        assertEquals(0, timer.totalDurationSec(), 0);
+        assertFalse(timer.isRunning());
+    }
+
+    private class TestInjector extends AmbientBrightnessStatsTracker.Injector {
+
+        private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+        private LocalDate mLocalDate = LocalDate.now();
+
+        public void incrementTime(long timeMillis) {
+            mElapsedRealtimeMillis += timeMillis;
+        }
+
+        public void incrementDate(int numDays) {
+            mLocalDate = mLocalDate.plusDays(numDays);
+        }
+
+        @Override
+        public long elapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        @Override
+        public int getUserSerialNumber(UserManager userManager, int userId) {
+            return userId + 10;
+        }
+
+        @Override
+        public int getUserId(UserManager userManager, int userSerialNumber) {
+            return userSerialNumber - 10;
+        }
+
+        @Override
+        public LocalDate getLocalDate() {
+            return LocalDate.from(mLocalDate);
+        }
+    }
+
+    private AmbientBrightnessStatsTracker getTestStatsTracker() {
+        return new AmbientBrightnessStatsTracker(
+                InstrumentationRegistry.getContext().getSystemService(UserManager.class),
+                mTestInjector);
+    }
+
+    private float[] getEmptyStatsArray() {
+        return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length];
+    }
+
+    private InputStream getInputStream(String data) {
+        return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index edc7d74..501f966 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.database.ContentObserver;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.display.BrightnessChangeEvent;
@@ -195,7 +194,9 @@
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
 
         final int systemUpdatedBrightness = 20;
-        notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/);
+        notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
+                0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
+                false /*isDefaultBrightnessConfig*/);
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         // No events because we filtered out our change.
         assertEquals(0, events.size());
@@ -285,7 +286,8 @@
                 + "lastNits=\"32.333\" "
                 + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
                 + "lux=\"32.2,31.1\" luxTimestamps=\""
-                + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+                + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+                + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />"
                 + "<event nits=\"71\" timestamp=\""
                 + Long.toString(someTimeAgo) + "\" packageName=\""
                 + "com.android.anapp\" user=\"11\" "
@@ -315,6 +317,9 @@
         assertFalse(event.nightMode);
         assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA);
         assertEquals("com.example.app", event.packageName);
+        assertTrue(event.isDefaultBrightnessConfig);
+        assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+        assertTrue(event.isUserSetBrightness);
 
         events = tracker.getEvents(1, true).getList();
         assertEquals(1, events.size());
@@ -329,6 +334,10 @@
         assertEquals(3235, event.colorTemperature);
         assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA);
         assertEquals("com.android.anapp", event.packageName);
+        // Not present in the event so default to false.
+        assertFalse(event.isDefaultBrightnessConfig);
+        assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
+        assertFalse(event.isUserSetBrightness);
     }
 
     @Test
@@ -379,7 +388,9 @@
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
         final long secondSensorTime = mInjector.currentTimeMillis();
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
-        notifyBrightnessChanged(mTracker, brightness);
+        notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
+                0.5f /*powerPolicyDim(*/, true /*hasUserBrightnessPoints*/,
+                false /*isDefaultBrightnessConfig*/);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         mTracker.writeEventsLocked(baos);
         mTracker.stop();
@@ -399,6 +410,9 @@
         assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3339, event.colorTemperature);
+        assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+        assertTrue(event.isUserSetBrightness);
+        assertFalse(event.isDefaultBrightnessConfig);
     }
 
     @Test
@@ -539,12 +553,16 @@
     }
 
     private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
-        notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/);
+        notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
+                1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
+                false /*isDefaultBrightnessConfig*/);
     }
 
     private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
-            boolean userInitiated) {
-        tracker.notifyBrightnessChanged(brightness, userInitiated);
+            boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
+            boolean isDefaultBrightnessConfig) {
+        tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
+                isUserSetBrightness, isDefaultBrightnessConfig);
         mInjector.waitForHandler();
     }
 
@@ -573,7 +591,6 @@
     private class TestInjector extends BrightnessTracker.Injector {
         SensorEventListener mSensorListener;
         BroadcastReceiver mBroadcastReceiver;
-        Map<String, Integer> mSystemIntSettings = new HashMap<>();
         Map<String, Integer> mSecureIntSettings = new HashMap<>();
         long mCurrentTimeMillis = System.currentTimeMillis();
         long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -639,7 +656,7 @@
         }
 
         @Override
-        public AtomicFile getFile() {
+        public AtomicFile getFile(String filename) {
             // Don't have the test write / read from anywhere.
             return null;
         }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
new file mode 100644
index 0000000..aad5295
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PersistentKeyChainSnapshotTest {
+
+    private static final String ALIAS = "some_key";
+    private static final String ALIAS2 = "another_key";
+    private static final byte[] RECOVERY_KEY_MATERIAL = "recovery_key_data"
+            .getBytes(StandardCharsets.UTF_8);
+    private static final byte[] KEY_MATERIAL = "app_key_data".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] PUBLIC_KEY = "public_key_data".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] ACCOUNT = "test_account".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
+    private static final int SNAPSHOT_VERSION = 2;
+    private static final int MAX_ATTEMPTS = 10;
+    private static final long COUNTER_ID = 123456789L;
+    private static final byte[] SERVER_PARAMS = "server_params".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] ZERO_BYTES = new byte[0];
+    private static final byte[] ONE_BYTE = new byte[]{(byte) 11};
+    private static final byte[] TWO_BYTES = new byte[]{(byte) 222,(byte) 222};
+
+    @Test
+    public void testWriteInt() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        writer.writeInt(Integer.MIN_VALUE);
+        writer.writeInt(Integer.MAX_VALUE);
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+        assertThat(reader.readInt()).isEqualTo(Integer.MIN_VALUE);
+        assertThat(reader.readInt()).isEqualTo(Integer.MAX_VALUE);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readInt());
+    }
+
+    @Test
+    public void testWriteLong() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        writer.writeLong(Long.MIN_VALUE);
+        writer.writeLong(Long.MAX_VALUE);
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+        assertThat(reader.readLong()).isEqualTo(Long.MIN_VALUE);
+        assertThat(reader.readLong()).isEqualTo(Long.MAX_VALUE);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readLong());
+    }
+
+    @Test
+    public void testWriteBytes() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        writer.writeBytes(ZERO_BYTES);
+        writer.writeBytes(ONE_BYTE);
+        writer.writeBytes(TWO_BYTES);
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+        assertThat(reader.readBytes()).isEqualTo(ZERO_BYTES);
+        assertThat(reader.readBytes()).isEqualTo(ONE_BYTE);
+        assertThat(reader.readBytes()).isEqualTo(TWO_BYTES);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readBytes());
+    }
+
+    @Test
+    public void testReadBytes_returnsNullArrayAsEmpty() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        writer.writeBytes(null);
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+        assertThat(reader.readBytes()).isEqualTo(new byte[]{}); // null -> empty array
+    }
+
+    @Test
+    public void testWriteKeyEntry() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
+                .setAlias(ALIAS)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setAccount(ACCOUNT)
+                .build();
+        writer.writeKeyEntry(entry);
+
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+
+        WrappedApplicationKey copy = reader.readKeyEntry();
+        assertThat(copy.getAlias()).isEqualTo(ALIAS);
+        assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
+        assertThat(copy.getAccount()).isEqualTo(ACCOUNT);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readKeyEntry());
+    }
+
+    public void testWriteProtectionParams() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
+        KeyChainProtectionParams protectionParams =  new KeyChainProtectionParams.Builder()
+                .setUserSecretType(1)
+                .setLockScreenUiFormat(2)
+                .setKeyDerivationParams(derivationParams)
+                .build();
+        writer.writeProtectionParams(protectionParams);
+
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+
+        KeyChainProtectionParams copy = reader.readProtectionParams();
+        assertThat(copy.getUserSecretType()).isEqualTo(1);
+        assertThat(copy.getLockScreenUiFormat()).isEqualTo(2);
+        assertThat(copy.getKeyDerivationParams().getSalt()).isEqualTo(SALT);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readProtectionParams());
+    }
+
+    public void testKeyChainSnapshot() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+
+        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
+
+        ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
+        protectionParamsList.add(new KeyChainProtectionParams.Builder()
+                .setUserSecretType(1)
+                .setLockScreenUiFormat(2)
+                .setKeyDerivationParams(derivationParams)
+                .build());
+
+        ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
+        appKeysList.add(new WrappedApplicationKey.Builder()
+                .setAlias(ALIAS)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setAccount(ACCOUNT)
+                .build());
+
+        KeyChainSnapshot snapshot =  new KeyChainSnapshot.Builder()
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setKeyChainProtectionParams(protectionParamsList)
+                .setEncryptedRecoveryKeyBlob(KEY_MATERIAL)
+                .setWrappedApplicationKeys(appKeysList)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setCounterId(COUNTER_ID)
+                .setServerParams(SERVER_PARAMS)
+                .setTrustedHardwarePublicKey(PUBLIC_KEY)
+                .build();
+
+        writer.writeKeyChainSnapshot(snapshot);
+
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+
+        KeyChainSnapshot copy = reader.readKeyChainSnapshot();
+        assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
+        assertThat(copy.getKeyChainProtectionParams()).hasSize(2);
+        assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
+        assertThat(copy.getKeyChainProtectionParams().get(1).getUserSecretType()).isEqualTo(2);
+        assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
+        assertThat(copy.getWrappedApplicationKeys()).hasSize(2);
+        assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
+        assertThat(copy.getWrappedApplicationKeys().get(1).getAlias()).isEqualTo(ALIAS2);
+        assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
+        assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
+        assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
+        assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readKeyChainSnapshot());
+
+        verifyDeserialize(snapshot);
+    }
+
+    public void testKeyChainSnapshot_withManyKeysAndProtectionParams() throws Exception {
+        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
+        writer.initWriter();
+
+        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
+
+        ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
+        protectionParamsList.add(new KeyChainProtectionParams.Builder()
+                .setUserSecretType(1)
+                .setLockScreenUiFormat(2)
+                .setKeyDerivationParams(derivationParams)
+                .build());
+        protectionParamsList.add(new KeyChainProtectionParams.Builder()
+                .setUserSecretType(2)
+                .setLockScreenUiFormat(3)
+                .setKeyDerivationParams(derivationParams)
+                .build());
+        ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
+        appKeysList.add(new WrappedApplicationKey.Builder()
+                .setAlias(ALIAS)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setAccount(ACCOUNT)
+                .build());
+        appKeysList.add(new WrappedApplicationKey.Builder()
+                .setAlias(ALIAS2)
+                .setEncryptedKeyMaterial(KEY_MATERIAL)
+                .setAccount(ACCOUNT)
+                .build());
+
+
+        KeyChainSnapshot snapshot =  new KeyChainSnapshot.Builder()
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setKeyChainProtectionParams(protectionParamsList)
+                .setEncryptedRecoveryKeyBlob(KEY_MATERIAL)
+                .setWrappedApplicationKeys(appKeysList)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setCounterId(COUNTER_ID)
+                .setServerParams(SERVER_PARAMS)
+                .setTrustedHardwarePublicKey(PUBLIC_KEY)
+                .build();
+
+        writer.writeKeyChainSnapshot(snapshot);
+
+        byte[] result = writer.getOutput();
+
+        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
+        reader.initReader(result);
+
+        KeyChainSnapshot copy = reader.readKeyChainSnapshot();
+        assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
+        assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
+        assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
+        assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
+        assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
+        assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
+        assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
+        assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
+
+        assertThrows(
+                IOException.class,
+                () -> reader.readKeyChainSnapshot());
+
+        verifyDeserialize(snapshot);
+    }
+
+    private void verifyDeserialize(KeyChainSnapshot snapshot) throws Exception {
+        byte[] serialized = PersistentKeyChainSnapshot.serialize(snapshot);
+        KeyChainSnapshot copy = PersistentKeyChainSnapshot.deserialize(serialized);
+        assertThat(copy.getSnapshotVersion())
+                .isEqualTo(snapshot.getSnapshotVersion());
+        assertThat(copy.getKeyChainProtectionParams().size())
+                .isEqualTo(copy.getKeyChainProtectionParams().size());
+        assertThat(copy.getEncryptedRecoveryKeyBlob())
+                .isEqualTo(snapshot.getEncryptedRecoveryKeyBlob());
+        assertThat(copy.getWrappedApplicationKeys().size())
+                .isEqualTo(snapshot.getWrappedApplicationKeys().size());
+        assertThat(copy.getMaxAttempts()).isEqualTo(snapshot.getMaxAttempts());
+        assertThat(copy.getCounterId()).isEqualTo(snapshot.getCounterId());
+        assertThat(copy.getServerParams()).isEqualTo(snapshot.getServerParams());
+        assertThat(copy.getTrustedHardwarePublicKey())
+                .isEqualTo(snapshot.getTrustedHardwarePublicKey());
+    }
+
+
+    public void testDeserialize_failsForNewerVersion() throws Exception {
+        byte[] newVersion = new byte[]{(byte) 2, (byte) 0, (byte) 0, (byte) 0};
+        assertThrows(
+                IOException.class,
+                () -> PersistentKeyChainSnapshot.deserialize(newVersion));
+    }
+
+    public void testDeserialize_failsForEmptyData() throws Exception {
+        byte[] empty = new byte[]{};
+        assertThrows(
+                IOException.class,
+                () -> PersistentKeyChainSnapshot.deserialize(empty));
+    }
+
+}
+
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 56d4b7e..857925b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -5149,7 +5149,8 @@
                     .forAllShortcuts(si -> {
                         switch (package1DisabledReason) {
                             case ShortcutInfo.DISABLED_REASON_VERSION_LOWER:
-                                assertEquals("This shortcut requires latest app",
+                                assertEquals("App version downgraded, or isn’t compatible"
+                                        + " with this shortcut",
                                         si.getDisabledMessage());
                                 break;
                             case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH:
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index f788cac..f7516b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -16,10 +16,18 @@
 package com.android.server.power.batterysaver;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
 import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
@@ -39,7 +47,11 @@
     private class BatterySavingStatsTestable extends BatterySavingStats {
         private long mTime = 1_000_000; // Some random starting time.
 
-        private int mBatteryLevel = 100;
+        private int mBatteryLevel = 1_000_000_000;
+
+        private BatterySavingStatsTestable() {
+            super(mMetricsLogger);
+        }
 
         @Override
         long injectCurrentTime() {
@@ -51,6 +63,11 @@
             return mBatteryLevel;
         }
 
+        @Override
+        int injectBatteryPercent() {
+            return mBatteryLevel / 10;
+        }
+
         void assertDumpable() {
             final ByteArrayOutputStream out = new ByteArrayOutputStream();
             dump(new PrintWriter(out), ""); // Just make sure it won't crash.
@@ -60,8 +77,8 @@
             mTime += 60_000 * minutes;
         }
 
-        void drainBattery(int percent) {
-            mBatteryLevel -= percent;
+        void drainBattery(int value) {
+            mBatteryLevel -= value;
             if (mBatteryLevel < 0) {
                 mBatteryLevel = 0;
             }
@@ -81,6 +98,8 @@
         }
     }
 
+    public MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
+
     @Test
     public void testAll() {
         final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
@@ -88,7 +107,7 @@
         target.assertDumpable();
 
         target.advanceClock(1);
-        target.drainBattery(2);
+        target.drainBattery(200);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -96,7 +115,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(4);
-        target.drainBattery(1);
+        target.drainBattery(100);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -104,7 +123,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(2);
-        target.drainBattery(5);
+        target.drainBattery(500);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -112,7 +131,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(4);
-        target.drainBattery(1);
+        target.drainBattery(100);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -120,7 +139,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(2);
-        target.drainBattery(5);
+        target.drainBattery(500);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -128,7 +147,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(3);
-        target.drainBattery(1);
+        target.drainBattery(100);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -136,7 +155,7 @@
                 DozeState.LIGHT);
 
         target.advanceClock(5);
-        target.drainBattery(1);
+        target.drainBattery(100);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -144,7 +163,7 @@
                 DozeState.DEEP);
 
         target.advanceClock(1);
-        target.drainBattery(2);
+        target.drainBattery(200);
 
         target.transitionState(
                 BatterySaverState.ON,
@@ -152,7 +171,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(1);
-        target.drainBattery(3);
+        target.drainBattery(300);
 
         target.transitionState(
                 BatterySaverState.OFF,
@@ -160,7 +179,7 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(3);
-        target.drainBattery(5);
+        target.drainBattery(500);
 
         target.transitionState(
                 BatterySaverState.ON,
@@ -168,12 +187,12 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(3);
-        target.drainBattery(5);
+        target.drainBattery(500);
 
         target.startCharging();
 
         target.advanceClock(5);
-        target.drainBattery(10);
+        target.drainBattery(1000);
 
         target.transitionState(
                 BatterySaverState.ON,
@@ -181,25 +200,120 @@
                 DozeState.NOT_DOZING);
 
         target.advanceClock(5);
-        target.drainBattery(1);
+        target.drainBattery(100);
 
         target.startCharging();
 
         target.assertDumpable();
 
         assertEquals(
-                "BS=0,I=0,D=0:{4m,10,150.00}\n" +
-                "BS=1,I=0,D=0:{0m,0,0.00}\n" +
-                "BS=0,I=1,D=0:{14m,8,34.29}\n" +
-                "BS=1,I=1,D=0:{9m,9,60.00}\n" +
-                "BS=0,I=0,D=1:{5m,1,12.00}\n" +
-                "BS=1,I=0,D=1:{0m,0,0.00}\n" +
-                "BS=0,I=1,D=1:{0m,0,0.00}\n" +
-                "BS=1,I=1,D=1:{0m,0,0.00}\n" +
-                "BS=0,I=0,D=2:{1m,2,120.00}\n" +
-                "BS=1,I=0,D=2:{0m,0,0.00}\n" +
-                "BS=0,I=1,D=2:{0m,0,0.00}\n" +
-                "BS=1,I=1,D=2:{0m,0,0.00}",
+                "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
+                "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
+                "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
+                "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
+                "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
+                "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+                "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
                 target.toDebugString());
     }
+
+    private void assertMetricsLog(String counter, int value) {
+        verify(mMetricsLogger, times(1)).count(eq(counter), eq(value));
+    }
+
+    @Test
+    public void testMetricsLogger() {
+        final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
+
+        target.advanceClock(1);
+        target.drainBattery(1000);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        reset(mMetricsLogger);
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
+        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        reset(mMetricsLogger);
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.DEEP);
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.LIGHT);
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+
+        target.transitionState(
+                BatterySaverState.ON,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
+        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
+
+        target.advanceClock(10);
+        target.drainBattery(10000);
+
+        reset(mMetricsLogger);
+        target.startCharging();
+
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
+        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        reset(mMetricsLogger);
+        target.transitionState(
+                BatterySaverState.ON,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+
+        target.advanceClock(1);
+        target.drainBattery(2000);
+
+        target.startCharging();
+
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
+        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index f860195..26a7313 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,12 +19,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.platform.test.annotations.Postsubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -137,6 +137,32 @@
     }
 
     @Test
+    public void testTimeout_scaled() throws Exception {
+        sWm.setAnimationScale(2, 5.0f);
+        try{
+            final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+            final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+                    new Point(50, 100), new Rect(50, 100, 150, 150));
+            adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+            mController.goodToGo();
+
+            mClock.fastForward(2500);
+            mHandler.timeAdvance();
+
+            verify(mMockRunner, never()).onAnimationCancelled();
+
+            mClock.fastForward(10000);
+            mHandler.timeAdvance();
+
+            verify(mMockRunner).onAnimationCancelled();
+            verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+        } finally {
+            sWm.setAnimationScale(2, 1.0f);
+        }
+
+    }
+
+    @Test
     public void testZeroAnimations() throws Exception {
         mController.goodToGo();
         verifyZeroInteractions(mMockRunner);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..51b019a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,50 @@
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link RootWindowContainer} class.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RootWindowContainerTests extends WindowTestsBase {
+    @Test
+    public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
+        // Add first stack we expect to be updated with configuration change.
+        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+        stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+
+        // Add second task that will be set for deferred removal that should not be returned
+        // with the configuration change.
+        final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+        deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+                new Rect(0, 0, 5, 5));
+        deferredDeletedStack.mDeferRemoval = true;
+
+        final Configuration override = new Configuration(
+                mDisplayContent.getOverrideConfiguration());
+        override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+
+        // Set display override.
+        final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+                        mDisplayContent.getDisplayId());
+
+        // Ensure only first stack is returned.
+        assertTrue(results.length == 1);
+        assertTrue(results[0] == stack.mStackId);
+    }
+}
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 99eb846..e36586e 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
@@ -210,6 +210,9 @@
         runCommand(instrumentation, "cmd package set-home-activity --user "
                 + instrumentation.getContext().getUserId() + " " + component,
                 result -> result.contains("Success"));
+        runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
+                        + instrumentation.getContext().getUserId(),
+                result -> result.contains("Success"));
     }
 
     public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 3475572..aabf9ea 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9ae6f00..6b6df29 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -117,7 +117,7 @@
 public class NotificationManagerServiceTest extends UiServiceTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
     private final int mUid = Binder.getCallingUid();
-    private NotificationManagerService mService;
+    private TestableNotificationManagerService mService;
     private INotificationManager mBinderService;
     private NotificationManagerInternal mInternalService;
     @Mock
@@ -152,17 +152,21 @@
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
+        int countSystemChecks = 0;
+
         public TestableNotificationManagerService(Context context) {
             super(context);
         }
 
         @Override
         protected boolean isCallingUidSystem() {
+            countSystemChecks++;
             return true;
         }
 
         @Override
         protected boolean isCallerSystemOrPhone() {
+            countSystemChecks++;
             return true;
         }
 
@@ -2429,4 +2433,18 @@
                         .setUid(user2.sbn.getUid())
                         .setLastNotified(user2.sbn.getPostTime())));
     }
+
+    @Test
+    public void testRestore() throws Exception {
+        int systemChecks = mService.countSystemChecks;
+        mBinderService.applyRestore(null, UserHandle.USER_SYSTEM);
+        assertEquals(1, mService.countSystemChecks - systemChecks);
+    }
+
+    @Test
+    public void testBackup() throws Exception {
+        int systemChecks = mService.countSystemChecks;
+        mBinderService.getBackupPayload(1);
+        assertEquals(1, mService.countSystemChecks - systemChecks);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 1606bd9..cfd155e 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -1,5 +1,7 @@
 package com.android.server.slice;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -11,7 +13,9 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -20,6 +24,7 @@
 import android.app.slice.SliceProvider;
 import android.app.slice.SliceSpec;
 import android.content.ContentProvider;
+import android.content.Context;
 import android.content.IContentProvider;
 import android.net.Uri;
 import android.os.Binder;
@@ -70,8 +75,8 @@
     @Before
     public void setup() {
         mSliceService = mock(SliceManagerService.class);
-        when(mSliceService.getLock()).thenReturn(new Object());
         when(mSliceService.getContext()).thenReturn(mContext);
+        when(mSliceService.getLock()).thenReturn(new Object());
         when(mSliceService.getHandler()).thenReturn(new Handler(TestableLooper.get(this).getLooper()));
         mContentProvider = mock(ContentProvider.class);
         mIContentProvider = mock(IContentProvider.class);
@@ -99,8 +104,11 @@
     }
 
     @Test
-    public void testSendPinnedOnCreate() throws RemoteException {
-        // When created, a pinned message should be sent.
+    public void testSendPinnedOnPin() throws RemoteException {
+        TestableLooper.get(this).processAllMessages();
+
+        // When pinned for the first time, a pinned message should be sent.
+        mPinnedSliceManager.pin("pkg", FIRST_SPECS);
         TestableLooper.get(this).processAllMessages();
 
         verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
@@ -111,10 +119,46 @@
     }
 
     @Test
+    public void testSendPinnedOnListen() throws RemoteException {
+        TestableLooper.get(this).processAllMessages();
+
+        // When a listener is added for the first time, a pinned message should be sent.
+        ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
+
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
+                argThat(b -> {
+                    assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
+                    return true;
+                }));
+    }
+
+    @Test
+    public void testNoSendPinnedWithoutPermission() throws RemoteException {
+        TestableLooper.get(this).processAllMessages();
+
+        // When a listener is added for the first time, a pinned message should be sent.
+        ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
+
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                false);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mIContentProvider, never()).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
+                any());
+    }
+
+    @Test
     public void testSendUnpinnedOnDestroy() throws RemoteException {
         TestableLooper.get(this).processAllMessages();
         clearInvocations(mIContentProvider);
 
+        mPinnedSliceManager.pin("pkg", FIRST_SPECS);
         mPinnedSliceManager.destroy();
         TestableLooper.get(this).processAllMessages();
 
@@ -127,39 +171,40 @@
 
     @Test
     public void testPkgPin() {
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
         mPinnedSliceManager.pin("pkg", FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
 
         assertTrue(mPinnedSliceManager.unpin("pkg"));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
     public void testMultiPkgPin() {
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
         mPinnedSliceManager.pin("pkg", FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
         mPinnedSliceManager.pin("pkg2", FIRST_SPECS);
 
         assertFalse(mPinnedSliceManager.unpin("pkg"));
         assertTrue(mPinnedSliceManager.unpin("pkg2"));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
     public void testListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
         when(listener.asBinder()).thenReturn(new Binder());
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
-        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
 
         assertTrue(mPinnedSliceManager.removeSliceListener(listener));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
@@ -170,15 +215,17 @@
         ISliceListener listener2 = mock(ISliceListener.class);
         Binder value2 = new Binder();
         when(listener2.asBinder()).thenReturn(value2);
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
-        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
-        mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
+        mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS,
+                true);
 
         assertFalse(mPinnedSliceManager.removeSliceListener(listener));
         assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
@@ -187,10 +234,11 @@
         IBinder binder = mock(IBinder.class);
         when(binder.isBinderAlive()).thenReturn(true);
         when(listener.asBinder()).thenReturn(binder);
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
-        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
 
         ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
         verify(binder).linkToDeath(arg.capture(), anyInt());
@@ -200,22 +248,23 @@
 
         verify(mSliceService).unlisten(eq(TEST_URI));
         verify(mSliceService).removePinnedSlice(eq(TEST_URI));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
     public void testPkgListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
         when(listener.asBinder()).thenReturn(new Binder());
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
-        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
-        assertTrue(mPinnedSliceManager.isPinned());
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
+        assertTrue(mPinnedSliceManager.hasPinOrListener());
         mPinnedSliceManager.pin("pkg", FIRST_SPECS);
 
         assertFalse(mPinnedSliceManager.removeSliceListener(listener));
         assertTrue(mPinnedSliceManager.unpin("pkg"));
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
 
     @Test
@@ -231,9 +280,10 @@
         when(mIContentProvider.call(anyString(), eq(SliceProvider.METHOD_SLICE), eq(null),
                 any())).thenReturn(b);
 
-        assertFalse(mPinnedSliceManager.isPinned());
+        assertFalse(mPinnedSliceManager.hasPinOrListener());
 
-        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                true);
 
         mPinnedSliceManager.onChange();
         TestableLooper.get(this).processAllMessages();
@@ -245,4 +295,30 @@
                 }));
         verify(listener).onSliceUpdated(eq(s));
     }
+
+    @Test
+    public void testRecheckPackage() throws RemoteException {
+        TestableLooper.get(this).processAllMessages();
+
+        ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
+
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
+                false);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mIContentProvider, never()).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
+                any());
+
+        when(mSliceService.checkAccess(any(), any(), anyInt(), anyInt()))
+                .thenReturn(PERMISSION_GRANTED);
+        mPinnedSliceManager.recheckPackage(mContext.getPackageName());
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
+                argThat(b -> {
+                    assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
+                    return true;
+                }));
+    }
 }
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
new file mode 100644
index 0000000..b784c60
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+@SmallTest
+public class SliceFullAccessListTest extends UiServiceTestCase {
+
+    private static final String TEST_XML = "<slice-access-list version=\"1\"><user "
+            + "user=\"0\"><pkg>pkg</pkg><pkg>pkg1</pkg></user><user "
+            + "user=\"1\"><pkg>pkg</pkg></user><user "
+            + "user=\"3\"><pkg>pkg2</pkg></user></slice-access-list>";
+
+    private SliceFullAccessList mAccessList;
+
+    @Before
+    public void setup() {
+        mAccessList = new SliceFullAccessList(mContext);
+    }
+
+    @Test
+    public void testNoDefaultAccess() {
+        assertFalse(mAccessList.hasFullAccess("pkg", 0));
+    }
+
+    @Test
+    public void testGrantAccess() {
+        mAccessList.grantFullAccess("pkg", 0);
+        assertTrue(mAccessList.hasFullAccess("pkg", 0));
+    }
+
+    @Test
+    public void testUserSeparation() {
+        mAccessList.grantFullAccess("pkg", 1);
+        assertFalse(mAccessList.hasFullAccess("pkg", 0));
+    }
+
+    @Test
+    public void testRemoveAccess() {
+        mAccessList.grantFullAccess("pkg", 0);
+        assertTrue(mAccessList.hasFullAccess("pkg", 0));
+
+        mAccessList.removeGrant("pkg", 0);
+        assertFalse(mAccessList.hasFullAccess("pkg", 0));
+    }
+
+    @Test
+    public void testSerialization() throws XmlPullParserException, IOException {
+        mAccessList.grantFullAccess("pkg", 0);
+        mAccessList.grantFullAccess("pkg1", 0);
+        mAccessList.grantFullAccess("pkg", 1);
+        mAccessList.grantFullAccess("pkg2", 3);
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+        out.setOutput(output, Encoding.UTF_8.name());
+        mAccessList.writeXml(out);
+        out.flush();
+
+        assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));
+    }
+
+    @Test
+    public void testDeSerialization() throws XmlPullParserException, IOException {
+        ByteArrayInputStream input = new ByteArrayInputStream(TEST_XML.getBytes());
+        XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+        parser.setInput(input, Encoding.UTF_8.name());
+
+        mAccessList.readXml(parser);
+
+        assertTrue(mAccessList.hasFullAccess("pkg", 0));
+        assertTrue(mAccessList.hasFullAccess("pkg1", 0));
+        assertTrue(mAccessList.hasFullAccess("pkg", 1));
+        assertTrue(mAccessList.hasFullAccess("pkg2", 3));
+
+        assertFalse(mAccessList.hasFullAccess("pkg3", 0));
+        assertFalse(mAccessList.hasFullAccess("pkg1", 1));
+        assertFalse(mAccessList.hasFullAccess("pkg", 3));
+        assertFalse(mAccessList.hasFullAccess("pkg", 2));
+    }
+}
\ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e3e5e3e..55ffea6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,7 +41,6 @@
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
-import android.hardware.usb.gadget.V1_0.GadgetFunction;
 import android.hardware.usb.gadget.V1_0.IUsbGadget;
 import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback;
 import android.hardware.usb.gadget.V1_0.Status;
@@ -68,6 +67,8 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.SomeArgs;
@@ -86,21 +87,20 @@
 import java.util.NoSuchElementException;
 import java.util.Scanner;
 import java.util.Set;
-import java.util.StringJoiner;
 
 /**
  * UsbDeviceManager manages USB state in device mode.
  */
 public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
 
-    private static final String TAG = "UsbDeviceManager";
+    private static final String TAG = UsbDeviceManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
     /**
      * The SharedPreference setting per user that stores the screen unlocked functions between
      * sessions.
      */
-    private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
+    static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
 
     /**
      * ro.bootmode value when phone boots into usual Android.
@@ -156,8 +156,6 @@
 
     private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
     private UsbHandler mHandler;
-    private boolean mBootCompleted;
-    private boolean mSystemReady;
 
     private final Object mLock = new Object();
 
@@ -165,22 +163,13 @@
     private final ContentResolver mContentResolver;
     @GuardedBy("mLock")
     private UsbProfileGroupSettingsManager mCurrentSettings;
-    private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
-    private boolean mUseUsbNotification;
-    private boolean mAdbEnabled;
-    private boolean mAudioSourceEnabled;
-    private boolean mMidiEnabled;
-    private int mMidiCard;
-    private int mMidiDevice;
+    @GuardedBy("mLock")
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
-    private final UsbAlsaManager mUsbAlsaManager;
-    private final UsbSettingsManager mSettingsManager;
-    private Intent mBroadcastedIntent;
-    private boolean mPendingBootBroadcast;
+    private final UEventObserver mUEventObserver;
+
     private static Set<Integer> sBlackListedInterfaces;
-    private SharedPreferences mSettings;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -213,7 +202,7 @@
     /*
      * Listens for uevent messages from the kernel to monitor the USB state
      */
-    private final UEventObserver mUEventObserver = new UEventObserver() {
+    private final class UsbUEventObserver extends UEventObserver {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
             if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
@@ -227,7 +216,7 @@
                 startAccessoryMode();
             }
         }
-    };
+    }
 
     @Override
     public void onKeyguardStateChanged(boolean isShowing) {
@@ -257,8 +246,6 @@
     public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
             UsbSettingsManager settingsManager) {
         mContext = context;
-        mUsbAlsaManager = alsaManager;
-        mSettingsManager = settingsManager;
         mContentResolver = context.getContentResolver();
         PackageManager pm = mContext.getPackageManager();
         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
@@ -274,16 +261,24 @@
             Slog.i(TAG, "USB GADGET HAL not present in the device", e);
         }
 
+        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+        boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
+        if (secureAdbEnabled && !dataEncrypted) {
+            mDebuggingManager = new UsbDebuggingManager(context);
+        }
+
         if (halNotPresent) {
             /**
              * Initialze the legacy UsbHandler
              */
-            mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext);
+            mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
+                    mDebuggingManager, alsaManager, settingsManager);
         } else {
             /**
              * Initialize HAL based UsbHandler
              */
-            mHandler = new UsbHandlerHal(FgThread.get().getLooper());
+            mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
+                    mDebuggingManager, alsaManager, settingsManager);
         }
 
         if (nativeIsStartRequested()) {
@@ -291,12 +286,6 @@
             startAccessoryMode();
         }
 
-        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
-        boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
-        if (secureAdbEnabled && !dataEncrypted) {
-            mDebuggingManager = new UsbDebuggingManager(context);
-        }
-
         BroadcastReceiver portReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -347,41 +336,35 @@
 
         mContext.registerReceiver(languageChangedReceiver,
                 new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+
+        // Watch for USB configuration changes
+        mUEventObserver = new UsbUEventObserver();
+        mUEventObserver.startObserving(USB_STATE_MATCH);
+        mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+        // register observer to listen for settings changes
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+                false, new AdbSettingsObserver());
     }
 
-    private UsbProfileGroupSettingsManager getCurrentSettings() {
+    UsbProfileGroupSettingsManager getCurrentSettings() {
         synchronized (mLock) {
             return mCurrentSettings;
         }
     }
 
+    String[] getAccessoryStrings() {
+        synchronized (mLock) {
+            return mAccessoryStrings;
+        }
+    }
+
     public void systemReady() {
         if (DEBUG) Slog.d(TAG, "systemReady");
 
         LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
 
-        mNotificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
-        // Ensure that the notification channels are set up
-        if (isTv()) {
-            // TV-specific notification channel
-            mNotificationManager.createNotificationChannel(
-                    new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
-                            mContext.getString(
-                                    com.android.internal.R.string
-                                            .adb_debugging_notification_channel_tv),
-                            NotificationManager.IMPORTANCE_HIGH));
-        }
-
-        // We do not show the USB notification if the primary volume supports mass storage.
-        // The legacy mass storage UI will be used instead.
-        boolean massStorageSupported;
-        final StorageManager storageManager = StorageManager.from(mContext);
-        final StorageVolume primary = storageManager.getPrimaryVolume();
-        massStorageSupported = primary != null && primary.allowMassStorage();
-        mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_usbChargingMessage);
         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
     }
 
@@ -410,21 +393,19 @@
         boolean enableAccessory = (mAccessoryStrings != null &&
                 mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
                 mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
-        String functions = null;
 
-        if (enableAccessory && enableAudio) {
-            functions = UsbManager.USB_FUNCTION_ACCESSORY + ","
-                    + UsbManager.USB_FUNCTION_AUDIO_SOURCE;
-        } else if (enableAccessory) {
-            functions = UsbManager.USB_FUNCTION_ACCESSORY;
-        } else if (enableAudio) {
-            functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;
+        long functions = UsbManager.FUNCTION_NONE;
+        if (enableAccessory) {
+            functions |= UsbManager.FUNCTION_ACCESSORY;
+        }
+        if (enableAudio) {
+            functions |= UsbManager.FUNCTION_AUDIO_SOURCE;
         }
 
-        if (functions != null) {
+        if (functions != UsbManager.FUNCTION_NONE) {
             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
                     ACCESSORY_REQUEST_TIMEOUT);
-            setCurrentFunctions(functions, false);
+            setCurrentFunctions(functions);
         }
     }
 
@@ -451,19 +432,7 @@
         }
     }
 
-    private boolean isTv() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    private SharedPreferences getPinnedSharedPrefs(Context context) {
-        final File prefsFile = new File(new File(
-                Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
-                        context.getUserId(), context.getPackageName()), "shared_prefs"),
-                UsbDeviceManager.class.getSimpleName() + ".xml");
-        return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
-    }
-
-    private abstract class UsbHandler extends Handler {
+    abstract static class UsbHandler extends Handler {
 
         // current USB state
         private boolean mConnected;
@@ -471,21 +440,40 @@
         private boolean mSourcePower;
         private boolean mSinkPower;
         private boolean mConfigured;
-        protected boolean mUsbDataUnlocked;
         private boolean mAudioAccessoryConnected;
         private boolean mAudioAccessorySupported;
-        protected String mCurrentFunctions;
-        protected boolean mCurrentFunctionsApplied;
+
         private UsbAccessory mCurrentAccessory;
         private int mUsbNotificationId;
         private boolean mAdbNotificationShown;
-        private int mCurrentUser;
         private boolean mUsbCharging;
         private boolean mHideUsbNotification;
         private boolean mSupportsAllCombinations;
-        private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE;
         private boolean mScreenLocked;
-        protected boolean mCurrentUsbFunctionsRequested;
+        private boolean mSystemReady;
+        private Intent mBroadcastedIntent;
+        private boolean mPendingBootBroadcast;
+        private boolean mAudioSourceEnabled;
+        private boolean mMidiEnabled;
+        private int mMidiCard;
+        private int mMidiDevice;
+
+        private final Context mContext;
+        private final UsbDebuggingManager mDebuggingManager;
+        private final UsbAlsaManager mUsbAlsaManager;
+        private final UsbSettingsManager mSettingsManager;
+        private NotificationManager mNotificationManager;
+
+        protected long mScreenUnlockedFunctions;
+        protected boolean mAdbEnabled;
+        protected boolean mBootCompleted;
+        protected boolean mCurrentFunctionsApplied;
+        protected boolean mUseUsbNotification;
+        protected long mCurrentFunctions;
+        protected final UsbDeviceManager mUsbDeviceManager;
+        protected final ContentResolver mContentResolver;
+        protected SharedPreferences mSettings;
+        protected int mCurrentUser;
         protected boolean mCurrentUsbFunctionsReceived;
 
         /**
@@ -494,31 +482,36 @@
          */
         protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
 
-        public UsbHandler(Looper looper) {
+        UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
+                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+                UsbSettingsManager settingsManager) {
             super(looper);
+            mContext = context;
+            mDebuggingManager = debuggingManager;
+            mUsbDeviceManager = deviceManager;
+            mUsbAlsaManager = alsaManager;
+            mSettingsManager = settingsManager;
+            mContentResolver = context.getContentResolver();
 
             mCurrentUser = ActivityManager.getCurrentUser();
+            mScreenUnlockedFunctions = UsbManager.FUNCTION_NONE;
             mScreenLocked = true;
 
             /*
              * Use the normal bootmode persistent prop to maintain state of adb across
              * all boot modes.
              */
-            mAdbEnabled = UsbManager.containsFunction(
-                    SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
-                    UsbManager.USB_FUNCTION_ADB);
+            mAdbEnabled = UsbHandlerLegacy.containsFunction(getSystemProperty(
+                    USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB);
 
-            /*
-             * Previous versions can set persist config to mtp/ptp but it does not
-             * get reset on OTA. Reset the property here instead.
-             */
-            String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
-            if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
-                    || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
-                SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
-                        UsbManager.removeFunction(UsbManager.removeFunction(persisted,
-                                UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
-            }
+            // We do not show the USB notification if the primary volume supports mass storage.
+            // The legacy mass storage UI will be used instead.
+            final StorageManager storageManager = StorageManager.from(mContext);
+            final StorageVolume primary = storageManager.getPrimaryVolume();
+
+            boolean massStorageSupported = primary != null && primary.allowMassStorage();
+            mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_usbChargingMessage);
         }
 
         public void sendMessage(int what, boolean arg) {
@@ -602,20 +595,14 @@
             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
             if (enable != mAdbEnabled) {
                 mAdbEnabled = enable;
-                String oldFunctions = mCurrentFunctions;
 
-                // Persist the adb setting
-                String newFunction = applyAdbFunction(SystemProperties.get(
-                        USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
-                SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
-
-                // Remove mtp from the config if file transfer is not enabled
-                if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) &&
-                        !mUsbDataUnlocked && enable) {
-                    oldFunctions = UsbManager.USB_FUNCTION_NONE;
+                if (enable) {
+                    setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB);
+                } else {
+                    setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, "");
                 }
 
-                setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked);
+                setEnabledFunctions(mCurrentFunctions, true);
                 updateAdbNotification(false);
             }
 
@@ -624,21 +611,7 @@
             }
         }
 
-        protected String applyAdbFunction(String functions) {
-            // Do not pass null pointer to the UsbManager.
-            // There isnt a check there.
-            if (functions == null) {
-                functions = "";
-            }
-            if (mAdbEnabled) {
-                functions = UsbManager.addFunction(functions, UsbManager.USB_FUNCTION_ADB);
-            } else {
-                functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
-            }
-            return functions;
-        }
-
-        private boolean isUsbTransferAllowed() {
+        protected boolean isUsbTransferAllowed() {
             UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
             return !userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
         }
@@ -650,12 +623,13 @@
 
             if (mConfigured && enteringAccessoryMode) {
                 // successfully entered accessory mode
-                if (mAccessoryStrings != null) {
-                    mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
+                String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings();
+                if (accessoryStrings != null) {
+                    mCurrentAccessory = new UsbAccessory(accessoryStrings);
                     Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
                     // defer accessoryAttached if system is not ready
                     if (mBootCompleted) {
-                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                        mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
                     } // else handle in boot completed
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -673,17 +647,24 @@
             // make sure accessory mode is off
             // and restore default functions
             Slog.d(TAG, "exited USB accessory mode");
-            setEnabledFunctions(null, false, false);
+            setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
 
             if (mCurrentAccessory != null) {
                 if (mBootCompleted) {
                     mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
                 }
                 mCurrentAccessory = null;
-                mAccessoryStrings = null;
             }
         }
 
+        protected SharedPreferences getPinnedSharedPrefs(Context context) {
+            final File prefsFile = new File(new File(
+                    Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+                            context.getUserId(), context.getPackageName()), "shared_prefs"),
+                    UsbDeviceManager.class.getSimpleName() + ".xml");
+            return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+        }
+
         private boolean isUsbStateChanged(Intent intent) {
             final Set<String> keySet = intent.getExtras().keySet();
             if (mBroadcastedIntent == null) {
@@ -706,7 +687,8 @@
             return false;
         }
 
-        protected void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
+        protected void updateUsbStateBroadcastIfNeeded(long functions,
+                boolean configChanged) {
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -716,18 +698,14 @@
             intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
             intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
-                    isUsbTransferAllowed() && mUsbDataUnlocked);
+                    isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
             intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
 
-            if (mCurrentFunctions != null) {
-                String[] functions = mCurrentFunctions.split(",");
-                for (int i = 0; i < functions.length; i++) {
-                    final String function = functions[i];
-                    if (UsbManager.USB_FUNCTION_NONE.equals(function)) {
-                        continue;
-                    }
-                    intent.putExtra(function, true);
-                }
+            long remainingFunctions = functions;
+            while (remainingFunctions != 0) {
+                intent.putExtra(UsbManager.usbFunctionsToString(
+                        Long.highestOneBit(remainingFunctions)), true);
+                remainingFunctions -= Long.highestOneBit(remainingFunctions);
             }
 
             // send broadcast intent only if the USB state has changed
@@ -739,18 +717,21 @@
             }
 
             if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            sendStickyBroadcast(intent);
             mBroadcastedIntent = intent;
         }
 
+        protected void sendStickyBroadcast(Intent intent) {
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
         private void updateUsbFunctions() {
             updateAudioSourceFunction();
             updateMidiFunction();
         }
 
         private void updateAudioSourceFunction() {
-            boolean enabled = UsbManager.containsFunction(mCurrentFunctions,
-                    UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+            boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_AUDIO_SOURCE) != 0;
             if (enabled != mAudioSourceEnabled) {
                 int card = -1;
                 int device = -1;
@@ -775,8 +756,7 @@
         }
 
         private void updateMidiFunction() {
-            boolean enabled = UsbManager.containsFunction(mCurrentFunctions,
-                    UsbManager.USB_FUNCTION_MIDI);
+            boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
             if (enabled != mMidiEnabled) {
                 if (enabled) {
                     Scanner scanner = null;
@@ -800,11 +780,21 @@
         }
 
         private void setScreenUnlockedFunctions() {
-            setEnabledFunctions(mScreenUnlockedFunctions, false,
-                    UsbManager.containsFunction(mScreenUnlockedFunctions,
-                            UsbManager.USB_FUNCTION_MTP)
-                            || UsbManager.containsFunction(mScreenUnlockedFunctions,
-                            UsbManager.USB_FUNCTION_PTP));
+            setEnabledFunctions(mScreenUnlockedFunctions, false);
+        }
+
+        /**
+         * Returns the functions that are passed down to the low level driver once adb and
+         * charging are accounted for.
+         */
+        long getAppliedFunctions(long functions) {
+            if (functions == UsbManager.FUNCTION_NONE) {
+                return getChargingFunctions();
+            }
+            if (mAdbEnabled) {
+                return functions | UsbManager.FUNCTION_ADB;
+            }
+            return functions;
         }
 
         @Override
@@ -817,10 +807,10 @@
                     updateUsbNotification(false);
                     updateAdbNotification(false);
                     if (mBootCompleted) {
-                        updateUsbStateBroadcastIfNeeded(false);
+                        updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
+                                false);
                     }
-                    if (UsbManager.containsFunction(mCurrentFunctions,
-                            UsbManager.USB_FUNCTION_ACCESSORY)) {
+                    if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
                         updateCurrentAccessory();
                     }
                     if (mBootCompleted) {
@@ -828,11 +818,10 @@
                                 && !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
                             // restore defaults when USB is disconnected
                             if (!mScreenLocked
-                                    && !UsbManager.USB_FUNCTION_NONE.equals(
-                                    mScreenUnlockedFunctions)) {
+                                    && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                                 setScreenUnlockedFunctions();
                             } else {
-                                setEnabledFunctions(null, !mAdbEnabled, false);
+                                setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
                             }
                         }
                         updateUsbFunctions();
@@ -867,7 +856,8 @@
                     updateUsbNotification(false);
                     if (mBootCompleted) {
                         if (mHostConnected || prevHostConnected) {
-                            updateUsbStateBroadcastIfNeeded(false);
+                            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
+                                    false);
                         }
                     } else {
                         mPendingBootBroadcast = true;
@@ -913,17 +903,17 @@
                     setAdbEnabled(msg.arg1 == 1);
                     break;
                 case MSG_SET_CURRENT_FUNCTIONS:
-                    String functions = (String) msg.obj;
-                    setEnabledFunctions(functions, false, msg.arg1 == 1);
+                    long functions = (Long) msg.obj;
+                    setEnabledFunctions(functions, false);
                     break;
                 case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS:
-                    mScreenUnlockedFunctions = (String) msg.obj;
+                    mScreenUnlockedFunctions = (Long) msg.obj;
                     SharedPreferences.Editor editor = mSettings.edit();
                     editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
-                            mCurrentUser), mScreenUnlockedFunctions);
+                            mCurrentUser),
+                            UsbManager.usbFunctionsToString(mScreenUnlockedFunctions));
                     editor.commit();
-                    if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals(
-                            mScreenUnlockedFunctions)) {
+                    if (!mScreenLocked && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                         // If the screen is unlocked, also set current functions.
                         setScreenUnlockedFunctions();
                     }
@@ -936,22 +926,21 @@
                     if (mSettings == null && !mScreenLocked) {
                         // Shared preferences aren't accessible until the user has been unlocked.
                         mSettings = getPinnedSharedPrefs(mContext);
-                        mScreenUnlockedFunctions = mSettings.getString(
+                        mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
+                                mSettings.getString(
                                 String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
-                                UsbManager.USB_FUNCTION_NONE);
+                                ""));
                     }
                     if (!mBootCompleted) {
                         break;
                     }
                     if (mScreenLocked) {
                         if (!mConnected) {
-                            setEnabledFunctions(null, false, false);
+                            setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                         }
                     } else {
-                        if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)
-                                && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions)
-                                || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions)
-                                && !mUsbDataUnlocked))) {
+                        if (mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE
+                                && mCurrentFunctions == UsbManager.FUNCTION_NONE) {
                             // Set the screen unlocked functions if current function is charging.
                             setScreenUnlockedFunctions();
                         }
@@ -959,13 +948,24 @@
                     break;
                 case MSG_UPDATE_USER_RESTRICTIONS:
                     // Restart the USB stack if USB transfer is enabled but no longer allowed.
-                    final boolean forceRestart = mUsbDataUnlocked
-                            && isUsbDataTransferActive()
-                            && !isUsbTransferAllowed();
-                    setEnabledFunctions(
-                            mCurrentFunctions, forceRestart, mUsbDataUnlocked && !forceRestart);
+                    if (isUsbDataTransferActive(mCurrentFunctions) && !isUsbTransferAllowed()) {
+                        setEnabledFunctions(UsbManager.FUNCTION_NONE, true);
+                    }
                     break;
                 case MSG_SYSTEM_READY:
+                    mNotificationManager = (NotificationManager)
+                            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+                    // Ensure that the notification channels are set up
+                    if (isTv()) {
+                        // TV-specific notification channel
+                        mNotificationManager.createNotificationChannel(
+                                new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+                                        mContext.getString(
+                                                com.android.internal.R.string
+                                                        .adb_debugging_notification_channel_tv),
+                                        NotificationManager.IMPORTANCE_HIGH));
+                    }
                     mSystemReady = true;
                     finishBoot();
                     break;
@@ -984,10 +984,14 @@
                         }
                         mCurrentUser = msg.arg1;
                         mScreenLocked = true;
-                        mScreenUnlockedFunctions = mSettings.getString(
-                                String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
-                                UsbManager.USB_FUNCTION_NONE);
-                        setEnabledFunctions(null, false, false);
+                        if (mSettings != null) {
+                            mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
+                                    mSettings.getString(
+                                            String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
+                                            mCurrentUser),
+                                    ""));
+                        }
+                        setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                     }
                     break;
                 }
@@ -995,9 +999,7 @@
                     if (DEBUG) {
                         Slog.v(TAG, "Accessory mode enter timeout: " + mConnected);
                     }
-                    if (!mConnected || !UsbManager.containsFunction(
-                            mCurrentFunctions,
-                            UsbManager.USB_FUNCTION_ACCESSORY)) {
+                    if (!mConnected || (mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) == 0) {
                         notifyAccessoryModeExit();
                     }
                     break;
@@ -1008,17 +1010,17 @@
         protected void finishBoot() {
             if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
                 if (mPendingBootBroadcast) {
-                    updateUsbStateBroadcastIfNeeded(false);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
                     mPendingBootBroadcast = false;
                 }
                 if (!mScreenLocked
-                        && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+                        && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                     setScreenUnlockedFunctions();
                 } else {
-                    setEnabledFunctions(null, false, false);
+                    setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                 }
                 if (mCurrentAccessory != null) {
-                    getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                    mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
                 }
                 if (mDebuggingManager != null) {
                     mDebuggingManager.setAdbEnabled(mAdbEnabled);
@@ -1026,8 +1028,8 @@
 
                 // make sure the ADB_ENABLED setting value matches the current state
                 try {
-                    Settings.Global.putInt(mContentResolver,
-                            Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+                    putGlobalSettings(mContentResolver, Settings.Global.ADB_ENABLED,
+                            mAdbEnabled ? 1 : 0);
                 } catch (SecurityException e) {
                     // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
                     // be changed.
@@ -1040,9 +1042,9 @@
             }
         }
 
-        private boolean isUsbDataTransferActive() {
-            return UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)
-                    || UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP);
+        protected boolean isUsbDataTransferActive(long functions) {
+            return (functions & UsbManager.FUNCTION_MTP) != 0
+                    || (functions & UsbManager.FUNCTION_PTP) != 0;
         }
 
         public UsbAccessory getCurrentAccessory() {
@@ -1051,7 +1053,7 @@
 
         protected void updateUsbNotification(boolean force) {
             if (mNotificationManager == null || !mUseUsbNotification
-                    || ("0".equals(SystemProperties.get("persist.charging.notify")))) {
+                    || ("0".equals(getSystemProperty("persist.charging.notify", "")))) {
                 return;
             }
 
@@ -1070,30 +1072,37 @@
             int id = 0;
             int titleRes = 0;
             Resources r = mContext.getResources();
+            CharSequence message = r.getText(
+                    com.android.internal.R.string.usb_notification_message);
             if (mAudioAccessoryConnected && !mAudioAccessorySupported) {
                 titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title;
                 id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED;
             } else if (mConnected) {
-                if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) {
+                if (mCurrentFunctions == UsbManager.FUNCTION_MTP) {
                     titleRes = com.android.internal.R.string.usb_mtp_notification_title;
                     id = SystemMessage.NOTE_USB_MTP;
-                } else if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) {
+                } else if (mCurrentFunctions == UsbManager.FUNCTION_PTP) {
                     titleRes = com.android.internal.R.string.usb_ptp_notification_title;
                     id = SystemMessage.NOTE_USB_PTP;
-                } else if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_MIDI)) {
+                } else if (mCurrentFunctions == UsbManager.FUNCTION_MIDI) {
                     titleRes = com.android.internal.R.string.usb_midi_notification_title;
                     id = SystemMessage.NOTE_USB_MIDI;
-                } else if (UsbManager.containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_ACCESSORY)) {
+                } else if (mCurrentFunctions == UsbManager.FUNCTION_RNDIS) {
+                    titleRes = com.android.internal.R.string.usb_tether_notification_title;
+                    id = SystemMessage.NOTE_USB_TETHER;
+                } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) {
                     titleRes = com.android.internal.R.string.usb_accessory_notification_title;
                     id = SystemMessage.NOTE_USB_ACCESSORY;
-                } else if (mSourcePower) {
-                    titleRes = com.android.internal.R.string.usb_supplying_notification_title;
-                    id = SystemMessage.NOTE_USB_SUPPLYING;
-                } else {
+                }
+                if (mSourcePower) {
+                    if (titleRes != 0) {
+                        message = r.getText(
+                                com.android.internal.R.string.usb_power_notification_message);
+                    } else {
+                        titleRes = com.android.internal.R.string.usb_supplying_notification_title;
+                        id = SystemMessage.NOTE_USB_SUPPLYING;
+                    }
+                } else if (titleRes == 0) {
                     titleRes = com.android.internal.R.string.usb_charging_notification_title;
                     id = SystemMessage.NOTE_USB_CHARGING;
                 }
@@ -1113,7 +1122,6 @@
                     mUsbNotificationId = 0;
                 }
                 if (id != 0) {
-                    CharSequence message;
                     CharSequence title = r.getText(titleRes);
                     PendingIntent pi;
                     String channel;
@@ -1123,12 +1131,10 @@
                             .usb_unsupported_audio_accessory_title) {
                         Intent intent = Intent.makeRestartActivityTask(
                                 new ComponentName("com.android.settings",
-                                        "com.android.settings.deviceinfo.UsbModeChooserActivity"));
+                                        "com.android.settings.Settings$UsbDetailsActivity"));
                         pi = PendingIntent.getActivityAsUser(mContext, 0,
                                 intent, 0, null, UserHandle.CURRENT);
                         channel = SystemNotificationChannels.USB;
-                        message = r.getText(
-                                com.android.internal.R.string.usb_notification_message);
                     } else {
                         final Intent intent = new Intent();
                         intent.setClassName("com.android.settings",
@@ -1184,7 +1190,7 @@
             final int titleRes = com.android.internal.R.string.adb_active_notification_title;
 
             if (mAdbEnabled && mConnected) {
-                if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
+                if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return;
 
                 if (force && mAdbNotificationShown) {
                     mAdbNotificationShown = false;
@@ -1230,20 +1236,41 @@
             }
         }
 
-        protected String getChargingFunctions() {
+        private boolean isTv() {
+            return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        }
+
+        protected long getChargingFunctions() {
             // if ADB is enabled, reset functions to ADB
             // else enable MTP as usual.
             if (mAdbEnabled) {
-                return UsbManager.USB_FUNCTION_ADB;
+                return UsbManager.FUNCTION_ADB;
             } else {
-                return UsbManager.USB_FUNCTION_MTP;
+                return UsbManager.FUNCTION_MTP;
             }
         }
 
-        public boolean isFunctionEnabled(String function) {
-            return UsbManager.containsFunction(mCurrentFunctions, function);
+        protected void setSystemProperty(String prop, String val) {
+            SystemProperties.set(prop, val);
         }
 
+        protected String getSystemProperty(String prop, String def) {
+            return SystemProperties.get(prop, def);
+        }
+
+        protected void putGlobalSettings(ContentResolver contentResolver, String setting, int val) {
+            Settings.Global.putInt(contentResolver, setting, val);
+        }
+
+        public long getEnabledFunctions() {
+            return mCurrentFunctions;
+        }
+
+        public long getScreenUnlockedFunctions() {
+            return mScreenUnlockedFunctions;
+        }
+
+
         public void dump(IndentingPrintWriter pw) {
             pw.println("USB Device State:");
             pw.println("  mCurrentFunctions: " + mCurrentFunctions);
@@ -1252,7 +1279,6 @@
             pw.println("  mScreenLocked: " + mScreenLocked);
             pw.println("  mConnected: " + mConnected);
             pw.println("  mConfigured: " + mConfigured);
-            pw.println("  mUsbDataUnlocked: " + mUsbDataUnlocked);
             pw.println("  mCurrentAccessory: " + mCurrentAccessory);
             pw.println("  mHostConnected: " + mHostConnected);
             pw.println("  mSourcePower: " + mSourcePower);
@@ -1275,12 +1301,10 @@
         /**
          * Evaluates USB function policies and applies the change accordingly.
          */
-        protected abstract void setEnabledFunctions(String functions, boolean forceRestart,
-                boolean usbDataUnlocked);
-
+        protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
     }
 
-    private final class UsbHandlerLegacy extends UsbHandler {
+    private static final class UsbHandlerLegacy extends UsbHandler {
         /**
          * The non-persistent property which stores the current USB settings.
          */
@@ -1293,46 +1317,44 @@
 
         private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
         private String mCurrentOemFunctions;
+        private String mCurrentFunctionsStr;
+        private boolean mUsbDataUnlocked;
 
-        UsbHandlerLegacy(Looper looper, Context context) {
-            super(looper);
+        UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
+                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+                UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
             try {
                 readOemUsbOverrideConfig(context);
                 // Restore default functions.
-                mCurrentOemFunctions = SystemProperties.get(getPersistProp(false),
+                mCurrentOemFunctions = getSystemProperty(getPersistProp(false),
                         UsbManager.USB_FUNCTION_NONE);
                 if (isNormalBoot()) {
-                    mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
+                    mCurrentFunctionsStr = getSystemProperty(USB_CONFIG_PROPERTY,
                             UsbManager.USB_FUNCTION_NONE);
-                    mCurrentFunctionsApplied = mCurrentFunctions.equals(
-                            SystemProperties.get(USB_STATE_PROPERTY));
+                    mCurrentFunctionsApplied = mCurrentFunctionsStr.equals(
+                            getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE));
                 } else {
-                    mCurrentFunctions = SystemProperties.get(getPersistProp(true),
+                    mCurrentFunctionsStr = getSystemProperty(getPersistProp(true),
                             UsbManager.USB_FUNCTION_NONE);
-                    mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
+                    mCurrentFunctionsApplied = getSystemProperty(USB_CONFIG_PROPERTY,
                             UsbManager.USB_FUNCTION_NONE).equals(
-                            SystemProperties.get(USB_STATE_PROPERTY));
+                            getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE));
                 }
+                // Mask out adb, since it is stored in mAdbEnabled
+                mCurrentFunctions = UsbManager.usbFunctionsFromString(mCurrentFunctionsStr)
+                        & ~UsbManager.FUNCTION_ADB;
                 mCurrentUsbFunctionsReceived = true;
 
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
-
-                // register observer to listen for settings changes
-                mContentResolver.registerContentObserver(
-                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                        false, new AdbSettingsObserver());
-
-                // Watch for USB configuration changes
-                mUEventObserver.startObserving(USB_STATE_MATCH);
-                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
             } catch (Exception e) {
                 Slog.e(TAG, "Error initializing UsbHandler", e);
             }
         }
 
         private void readOemUsbOverrideConfig(Context context) {
-            String[] configList = mContext.getResources().getStringArray(
+            String[] configList = context.getResources().getStringArray(
                     com.android.internal.R.array.config_oemUsbModeOverride);
 
             if (configList != null) {
@@ -1367,7 +1389,7 @@
                 return usbFunctions;
             }
 
-            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
             Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
 
             Map<String, Pair<String, String>> overridesMap =
@@ -1386,25 +1408,22 @@
                     if (!overrideFunctions.second.equals("")) {
                         String newFunction;
                         if (mAdbEnabled) {
-                            newFunction = UsbManager.addFunction(overrideFunctions.second,
+                            newFunction = addFunction(overrideFunctions.second,
                                     UsbManager.USB_FUNCTION_ADB);
                         } else {
                             newFunction = overrideFunctions.second;
                         }
                         Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
                                 + getPersistProp(false));
-                        SystemProperties.set(getPersistProp(false),
-                                newFunction);
+                        setSystemProperty(getPersistProp(false), newFunction);
                     }
                     return overrideFunctions.first;
                 } else if (mAdbEnabled) {
-                    String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
+                    String newFunction = addFunction(UsbManager.USB_FUNCTION_NONE,
                             UsbManager.USB_FUNCTION_ADB);
-                    SystemProperties.set(getPersistProp(false),
-                            newFunction);
+                    setSystemProperty(getPersistProp(false), newFunction);
                 } else {
-                    SystemProperties.set(getPersistProp(false),
-                            UsbManager.USB_FUNCTION_NONE);
+                    setSystemProperty(getPersistProp(false), UsbManager.USB_FUNCTION_NONE);
                 }
             }
             // return passed in functions as is.
@@ -1417,7 +1436,7 @@
             String value = null;
             for (int i = 0; i < 20; i++) {
                 // State transition is done when sys.usb.state is set to the new configuration
-                value = SystemProperties.get(USB_STATE_PROPERTY);
+                value = getSystemProperty(USB_STATE_PROPERTY, "");
                 if (state.equals(value)) return true;
                 SystemClock.sleep(50);
             }
@@ -1432,14 +1451,14 @@
              * we always set it due to b/23631400, where adbd was getting killed
              * and not restarted due to property timeouts on some devices
              */
-            SystemProperties.set(USB_CONFIG_PROPERTY, config);
+            setSystemProperty(USB_CONFIG_PROPERTY, config);
         }
 
         @Override
-        protected void setEnabledFunctions(String functions, boolean forceRestart,
-                boolean usbDataUnlocked) {
+        protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
+            boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
             if (DEBUG) {
-                Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+                Slog.d(TAG, "setEnabledFunctions functions=" + usbFunctions + ", "
                         + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
             }
 
@@ -1452,9 +1471,9 @@
             /**
              * Try to set the enabled functions.
              */
-            final String oldFunctions = mCurrentFunctions;
+            final long oldFunctions = mCurrentFunctions;
             final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
-            if (trySetEnabledFunctions(functions, forceRestart)) {
+            if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
                 return;
             }
 
@@ -1464,7 +1483,7 @@
              * user restrictions independently of any other new functions we were
              * trying to activate.
              */
-            if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
+            if (oldFunctionsApplied && oldFunctions != usbFunctions) {
                 Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
                 if (trySetEnabledFunctions(oldFunctions, false)) {
                     return;
@@ -1475,7 +1494,7 @@
              * Still didn't work.  Try to restore the default functions.
              */
             Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
-            if (trySetEnabledFunctions(null, false)) {
+            if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
                 return;
             }
 
@@ -1484,7 +1503,7 @@
              * Try to get ADB working if enabled.
              */
             Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
-            if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
+            if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
                 return;
             }
 
@@ -1495,30 +1514,49 @@
         }
 
         private boolean isNormalBoot() {
-            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
             return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
         }
 
-        private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
+        protected String applyAdbFunction(String functions) {
+            // Do not pass null pointer to the UsbManager.
+            // There isn't a check there.
+            if (functions == null) {
+                functions = "";
+            }
+            if (mAdbEnabled) {
+                functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
+            } else {
+                functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
+            }
+            return functions;
+        }
+
+        private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
+            String functions = null;
+            if (usbFunctions != UsbManager.FUNCTION_NONE) {
+                functions = UsbManager.usbFunctionsToString(usbFunctions);
+            }
+            mCurrentFunctions = usbFunctions;
             if (functions == null || applyAdbFunction(functions)
                     .equals(UsbManager.USB_FUNCTION_NONE)) {
-                functions = getChargingFunctions();
+                functions = UsbManager.usbFunctionsToString(getChargingFunctions());
             }
             functions = applyAdbFunction(functions);
 
             String oemFunctions = applyOemOverrideFunction(functions);
 
-            if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
-                SystemProperties.set(getPersistProp(true), functions);
+            if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
+                setSystemProperty(getPersistProp(true), functions);
             }
 
             if ((!functions.equals(oemFunctions)
                     && !mCurrentOemFunctions.equals(oemFunctions))
-                    || !mCurrentFunctions.equals(functions)
+                    || !mCurrentFunctionsStr.equals(functions)
                     || !mCurrentFunctionsApplied
                     || forceRestart) {
                 Slog.i(TAG, "Setting USB config to " + functions);
-                mCurrentFunctions = functions;
+                mCurrentFunctionsStr = functions;
                 mCurrentOemFunctions = oemFunctions;
                 mCurrentFunctionsApplied = false;
 
@@ -1538,12 +1576,12 @@
                 setUsbConfig(oemFunctions);
 
                 if (mBootCompleted
-                        && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
-                        || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+                        && (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+                        || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
                     /**
                      * Start up dependent services.
                      */
-                    updateUsbStateBroadcastIfNeeded(true);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
                 }
 
                 if (!waitForState(oemFunctions)) {
@@ -1557,7 +1595,7 @@
         }
 
         private String getPersistProp(boolean functions) {
-            String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+            String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
             String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
             if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
                 if (functions) {
@@ -1568,9 +1606,54 @@
             }
             return persistProp;
         }
+
+        private static String addFunction(String functions, String function) {
+            if (UsbManager.USB_FUNCTION_NONE.equals(functions)) {
+                return function;
+            }
+            if (!containsFunction(functions, function)) {
+                if (functions.length() > 0) {
+                    functions += ",";
+                }
+                functions += function;
+            }
+            return functions;
+        }
+
+        private static String removeFunction(String functions, String function) {
+            String[] split = functions.split(",");
+            for (int i = 0; i < split.length; i++) {
+                if (function.equals(split[i])) {
+                    split[i] = null;
+                }
+            }
+            if (split.length == 1 && split[0] == null) {
+                return UsbManager.USB_FUNCTION_NONE;
+            }
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < split.length; i++) {
+                String s = split[i];
+                if (s != null) {
+                    if (builder.length() > 0) {
+                        builder.append(",");
+                    }
+                    builder.append(s);
+                }
+            }
+            return builder.toString();
+        }
+
+        static boolean containsFunction(String functions, String function) {
+            int index = functions.indexOf(function);
+            if (index < 0) return false;
+            if (index > 0 && functions.charAt(index - 1) != ',') return false;
+            int charAfter = index + function.length();
+            if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+            return true;
+        }
     }
 
-    private final class UsbHandlerHal extends UsbHandler {
+    private static final class UsbHandlerHal extends UsbHandler {
 
         /**
          * Proxy object for the usb gadget hal daemon.
@@ -1627,9 +1710,12 @@
          */
         protected static final String ADBD = "adbd";
 
+        protected boolean mCurrentUsbFunctionsRequested;
 
-        UsbHandlerHal(Looper looper) {
-            super(looper);
+        UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
+                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+                UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
             try {
                 ServiceNotification serviceNotification = new ServiceNotification();
 
@@ -1645,25 +1731,12 @@
                     mGadgetProxy = IUsbGadget.getService(true);
                     mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                             USB_GADGET_HAL_DEATH_COOKIE);
-                    mCurrentFunctions = UsbManager.USB_FUNCTION_NONE;
+                    mCurrentFunctions = UsbManager.FUNCTION_NONE;
                     mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                     mCurrentUsbFunctionsRequested = true;
                 }
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
-
-                /**
-                 * Register observer to listen for settings changes.
-                 */
-                mContentResolver.registerContentObserver(
-                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                        false, new AdbSettingsObserver());
-
-                /**
-                 * Watch for USB configuration changes.
-                 */
-                mUEventObserver.startObserving(USB_STATE_MATCH);
-                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
             } catch (NoSuchElementException e) {
                 Slog.e(TAG, "Usb gadget hal not found", e);
             } catch (RemoteException e) {
@@ -1696,7 +1769,7 @@
                         mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                                 USB_GADGET_HAL_DEATH_COOKIE);
                         if (!mCurrentFunctionsApplied) {
-                            setCurrentFunctions(mCurrentFunctions, mUsbDataUnlocked);
+                            setEnabledFunctions(mCurrentFunctions, false);
                         }
                     } catch (NoSuchElementException e) {
                         Slog.e(TAG, "Usb gadget hal not found", e);
@@ -1711,12 +1784,12 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_SET_CHARGING_FUNCTIONS:
-                    setEnabledFunctions(null, false, mUsbDataUnlocked);
+                    setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                     break;
                 case MSG_SET_FUNCTIONS_TIMEOUT:
                     Slog.e(TAG, "Set functions timed out! no reply from usb hal");
                     if (msg.arg1 != 1) {
-                        setEnabledFunctions(null, false, mUsbDataUnlocked);
+                        setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                     }
                     break;
                 case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1725,7 +1798,8 @@
 
                     if (mCurrentUsbFunctionsRequested) {
                         Slog.e(TAG, "updating mCurrentFunctions");
-                        mCurrentFunctions = functionListToString((Long) msg.obj);
+                        // Mask out adb, since it is stored in mAdbEnabled
+                        mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
                         Slog.e(TAG,
                                 "mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
                         mCurrentFunctionsApplied = msg.arg1 == 1;
@@ -1737,7 +1811,7 @@
                      * Dont force to default when the configuration is already set to default.
                      */
                     if (msg.arg1 != 1) {
-                        setEnabledFunctions(null, !mAdbEnabled, false);
+                        setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
                     }
                     break;
                 default:
@@ -1789,70 +1863,7 @@
             }
         }
 
-        private long stringToFunctionList(String config) {
-            long functionsMask = 0;
-            String[] functions = config.split(",");
-            for (int i = 0; i < functions.length; i++) {
-                switch (functions[i]) {
-                    case "none":
-                        functionsMask |= GadgetFunction.NONE;
-                        break;
-                    case "adb":
-                        functionsMask |= GadgetFunction.ADB;
-                        break;
-                    case "mtp":
-                        functionsMask |= GadgetFunction.MTP;
-                        break;
-                    case "ptp":
-                        functionsMask |= GadgetFunction.PTP;
-                        break;
-                    case "midi":
-                        functionsMask |= GadgetFunction.MIDI;
-                        break;
-                    case "accessory":
-                        functionsMask |= GadgetFunction.ACCESSORY;
-                        break;
-                    case "rndis":
-                        functionsMask |= GadgetFunction.RNDIS;
-                        break;
-                }
-            }
-            return functionsMask;
-        }
-
-        private String functionListToString(Long functionList) {
-            StringJoiner functions = new StringJoiner(",");
-            if (functionList == GadgetFunction.NONE) {
-                functions.add("none");
-                return functions.toString();
-            }
-            if ((functionList & GadgetFunction.ADB) != 0) {
-                functions.add("adb");
-            }
-            if ((functionList & GadgetFunction.MTP) != 0) {
-                functions.add("mtp");
-            }
-            if ((functionList & GadgetFunction.PTP) != 0) {
-                functions.add("ptp");
-            }
-            if ((functionList & GadgetFunction.MIDI) != 0) {
-                functions.add("midi");
-            }
-            if ((functionList & GadgetFunction.ACCESSORY) != 0) {
-                functions.add("accessory");
-            }
-            if ((functionList & GadgetFunction.RNDIS) != 0) {
-                functions.add("rndis");
-            }
-            /**
-             * Remove the trailing comma.
-             */
-            return functions.toString();
-        }
-
-
-        private void setUsbConfig(String config, boolean chargingFunctions) {
-            Long functions = stringToFunctionList(config);
+        private void setUsbConfig(long config, boolean chargingFunctions) {
             if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest);
             /**
              * Cancel any ongoing requests, if present.
@@ -1867,20 +1878,20 @@
                     return;
                 }
                 try {
-                    if ((functions & GadgetFunction.ADB) != 0) {
+                    if ((config & UsbManager.FUNCTION_ADB) != 0) {
                         /**
                          * Start adbd if ADB function is included in the configuration.
                          */
-                        SystemProperties.set(CTL_START, ADBD);
+                        setSystemProperty(CTL_START, ADBD);
                     } else {
                         /**
                          * Stop adbd otherwise.
                          */
-                        SystemProperties.set(CTL_STOP, ADBD);
+                        setSystemProperty(CTL_STOP, ADBD);
                     }
                     UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
-                            functions, chargingFunctions);
-                    mGadgetProxy.setCurrentUsbFunctions(functions, usbGadgetCallback,
+                            config, chargingFunctions);
+                    mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
                             SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
                     sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
                             SET_FUNCTIONS_TIMEOUT_MS);
@@ -1894,49 +1905,29 @@
         }
 
         @Override
-        protected void setEnabledFunctions(String functions, boolean forceRestart,
-                boolean usbDataUnlocked) {
+        protected void setEnabledFunctions(long functions, boolean forceRestart) {
             if (DEBUG) {
                 Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
-                        + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+                        + "forceRestart=" + forceRestart);
             }
-
-            if (usbDataUnlocked != mUsbDataUnlocked) {
-                mUsbDataUnlocked = usbDataUnlocked;
-                updateUsbNotification(false);
-                forceRestart = true;
-            }
-
-            trySetEnabledFunctions(functions, forceRestart);
-        }
-
-        private void trySetEnabledFunctions(String functions, boolean forceRestart) {
-            boolean chargingFunctions = false;
-
-            if (functions == null || applyAdbFunction(functions)
-                    .equals(UsbManager.USB_FUNCTION_NONE)) {
-                functions = getChargingFunctions();
-                chargingFunctions = true;
-            }
-            functions = applyAdbFunction(functions);
-
-            if (!mCurrentFunctions.equals(functions)
+            if (mCurrentFunctions != functions
                     || !mCurrentFunctionsApplied
                     || forceRestart) {
-                Slog.i(TAG, "Setting USB config to " + functions);
+                Slog.i(TAG, "Setting USB config to " + UsbManager.usbFunctionsToString(functions));
                 mCurrentFunctions = functions;
                 mCurrentFunctionsApplied = false;
                 // set the flag to false as that would be stale value
                 mCurrentUsbFunctionsRequested = false;
 
-                // Set the new USB configuration.
-                setUsbConfig(mCurrentFunctions, chargingFunctions);
+                boolean chargingFunctions = functions == UsbManager.FUNCTION_NONE;
+                functions = getAppliedFunctions(functions);
 
-                if (mBootCompleted
-                        && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
-                        || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+                // Set the new USB configuration.
+                setUsbConfig(functions, chargingFunctions);
+
+                if (mBootCompleted && isUsbDataTransferActive(functions)) {
                     // Start up dependent services.
-                    updateUsbStateBroadcastIfNeeded(true);
+                    updateUsbStateBroadcastIfNeeded(functions, true);
                 }
             }
         }
@@ -1968,27 +1959,37 @@
         return nativeOpenAccessory();
     }
 
-    /**
-     * Checks whether the function is present in the USB configuration.
-     *
-     * @param function function to be checked.
-     */
-    public boolean isFunctionEnabled(String function) {
-        return mHandler.isFunctionEnabled(function);
+    public long getCurrentFunctions() {
+        return mHandler.getEnabledFunctions();
+    }
+
+    public long getScreenUnlockedFunctions() {
+        return mHandler.getScreenUnlockedFunctions();
     }
 
     /**
      * Adds function to the current USB configuration.
      *
-     * @param functions name of the USB function, or null to restore the default function.
-     * @param usbDataUnlocked whether user data is accessible.
+     * @param functions The functions to set, or empty to set the charging function.
      */
-    public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
+    public void setCurrentFunctions(long functions) {
         if (DEBUG) {
-            Slog.d(TAG, "setCurrentFunctions(" + functions + ", "
-                    + usbDataUnlocked + ")");
+            Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")");
         }
-        mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
+        if (functions == UsbManager.FUNCTION_NONE) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING);
+        } else if (functions == UsbManager.FUNCTION_MTP) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);
+        } else if (functions == UsbManager.FUNCTION_PTP) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP);
+        } else if (functions == UsbManager.FUNCTION_MIDI) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI);
+        } else if (functions == UsbManager.FUNCTION_RNDIS) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);
+        } else if (functions == UsbManager.FUNCTION_ACCESSORY) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);
+        }
+        mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
     }
 
     /**
@@ -1996,9 +1997,10 @@
      *
      * @param functions Functions to set.
      */
-    public void setScreenUnlockedFunctions(String functions) {
+    public void setScreenUnlockedFunctions(long functions) {
         if (DEBUG) {
-            Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")");
+            Slog.d(TAG, "setScreenUnlockedFunctions("
+                    + UsbManager.usbFunctionsToString(functions) + ")");
         }
         mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
     }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 1a20819..2f6e531 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -382,59 +382,44 @@
     }
 
     @Override
+    public void setCurrentFunctions(long functions) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
+        Preconditions.checkState(mDeviceManager != null);
+        mDeviceManager.setCurrentFunctions(functions);
+    }
+
+    @Override
+    public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+        setCurrentFunctions(UsbManager.usbFunctionsFromString(functions));
+    }
+
+    @Override
     public boolean isFunctionEnabled(String function) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        return mDeviceManager != null && mDeviceManager.isFunctionEnabled(function);
+        return (getCurrentFunctions() & UsbManager.usbFunctionsFromString(function)) != 0;
     }
 
     @Override
-    public void setCurrentFunction(String function, boolean usbDataUnlocked) {
+    public long getCurrentFunctions() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
-        if (!isSupportedCurrentFunction(function)) {
-            Slog.w(TAG, "Caller of setCurrentFunction() requested unsupported USB function: "
-                    + function);
-            function = UsbManager.USB_FUNCTION_NONE;
-        }
-
-        if (mDeviceManager != null) {
-            mDeviceManager.setCurrentFunctions(function, usbDataUnlocked);
-        } else {
-            throw new IllegalStateException("USB device mode not supported");
-        }
+        Preconditions.checkState(mDeviceManager != null);
+        return mDeviceManager.getCurrentFunctions();
     }
 
     @Override
-    public void setScreenUnlockedFunctions(String function) {
+    public void setScreenUnlockedFunctions(long functions) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
+        Preconditions.checkState(mDeviceManager != null);
 
-        if (!isSupportedCurrentFunction(function)) {
-            Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:"
-                    + function);
-            function = UsbManager.USB_FUNCTION_NONE;
-        }
-
-        if (mDeviceManager != null) {
-            mDeviceManager.setScreenUnlockedFunctions(function);
-        } else {
-            throw new IllegalStateException("USB device mode not supported");
-        }
+        mDeviceManager.setScreenUnlockedFunctions(functions);
     }
 
-    private static boolean isSupportedCurrentFunction(String function) {
-        if (function == null) return true;
-
-        switch (function) {
-            case UsbManager.USB_FUNCTION_NONE:
-            case UsbManager.USB_FUNCTION_AUDIO_SOURCE:
-            case UsbManager.USB_FUNCTION_MIDI:
-            case UsbManager.USB_FUNCTION_MTP:
-            case UsbManager.USB_FUNCTION_PTP:
-            case UsbManager.USB_FUNCTION_RNDIS:
-                return true;
-        }
-
-        return false;
+    @Override
+    public long getScreenUnlockedFunctions() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        Preconditions.checkState(mDeviceManager != null);
+        return mDeviceManager.getScreenUnlockedFunctions();
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 0474362..dfaaab9 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Signal strength related information.
  */
@@ -293,9 +295,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return ((mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
-                + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum));
+        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 4137853..f68d2ca 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * GSM signal strength related information.
  */
@@ -185,8 +187,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 0d07a40..6ffc8b6 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * LTE signal strength related information.
  */
@@ -231,10 +233,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mRsrp * primeNum)
-                + (mRsrq * primeNum) + (mRssnr * primeNum) + (mCqi * primeNum)
-                + (mTimingAdvance * primeNum);
+        return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index b94b01d..2cd56b8 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Wcdma signal strength related information.
  */
@@ -156,8 +158,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 90a3677..1176491 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -22,13 +22,13 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -1340,6 +1340,39 @@
     }
 
     /** @hide */
+    public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+        switch(rt) {
+            case RIL_RADIO_TECHNOLOGY_GPRS:
+            case RIL_RADIO_TECHNOLOGY_EDGE:
+            case RIL_RADIO_TECHNOLOGY_GSM:
+                return AccessNetworkType.GERAN;
+            case RIL_RADIO_TECHNOLOGY_UMTS:
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
+            case RIL_RADIO_TECHNOLOGY_HSPA:
+            case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return AccessNetworkType.UTRAN;
+            case RIL_RADIO_TECHNOLOGY_IS95A:
+            case RIL_RADIO_TECHNOLOGY_IS95B:
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
+                return AccessNetworkType.CDMA2000;
+            case RIL_RADIO_TECHNOLOGY_LTE:
+            case RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return AccessNetworkType.EUTRAN;
+            case RIL_RADIO_TECHNOLOGY_IWLAN:
+                return AccessNetworkType.IWLAN;
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+            default:
+                return AccessNetworkType.UNKNOWN;
+        }
+    }
+
+    /** @hide */
     public int getDataNetworkType() {
         return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0239fcf..5c290da 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4463,7 +4463,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.iccTransmitApduBasicChannel(subId, cla,
+                return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla,
                     instruction, p1, p2, p3, data);
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -4952,28 +4952,6 @@
         }
     }
 
-    /**
-     * Returns the response of ISIM Authetification through RIL.
-     * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
-     * @return the response of ISIM Authetification, or null if not available
-     * @hide
-     * @deprecated
-     * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
-     */
-    public String getIsimChallengeResponse(String nonce){
-        try {
-            IPhoneSubInfo info = getSubscriberInfo();
-            if (info == null)
-                return null;
-            return info.getIsimChallengeResponse(nonce);
-        } catch (RemoteException ex) {
-            return null;
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            return null;
-        }
-    }
-
     // ICC SIM Application Types
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index ef3a183..25f5133 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -27,6 +27,7 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Description of the response of a setup data call connection request.
@@ -220,17 +221,8 @@
 
     @Override
     public int hashCode() {
-        return mStatus * 31
-                + mSuggestedRetryTime * 37
-                + mCid * 41
-                + mActive * 43
-                + mType.hashCode() * 47
-                + mIfname.hashCode() * 53
-                + mAddresses.hashCode() * 59
-                + mDnses.hashCode() * 61
-                + mGateways.hashCode() * 67
-                + mPcscfs.hashCode() * 71
-                + mMtu * 73;
+        return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses,
+                mDnses, mGateways, mPcscfs, mMtu);
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index f8a040d..0ed0820 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -202,14 +202,6 @@
     String[] getIsimPcscf(int subId);
 
     /**
-     * TODO: Deprecate and remove this interface. Superceded by getIccsimChallengeResponse.
-     * Returns the response of ISIM Authetification through RIL.
-     * @return the response of ISIM Authetification, or null if
-     *     the Authentification hasn't been successed or isn't present iphonesubinfo.
-     */
-    String getIsimChallengeResponse(String nonce);
-
-    /**
      * Returns the response of the SIM application on the UICC to authentication
      * challenge/response algorithm. The data string and challenge response are
      * Base64 encoded Strings.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 24c497c..2b4c059 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -681,6 +681,7 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * @param subId The subscription to use.
+     * @param callingPackage the name of the package making the call.
      * @param cla Class of the APDU command.
      * @param instruction Instruction of the APDU command.
      * @param p1 P1 value of the APDU command.
@@ -691,7 +692,7 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      */
-    String iccTransmitApduBasicChannel(int subId, int cla, int instruction,
+    String iccTransmitApduBasicChannel(int subId, String callingPackage, int cla, int instruction,
             int p1, int p2, int p3, String data);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cdee9e6..a3a3080 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -220,11 +220,6 @@
     String SETUP_DATA_PROTOCOL_IPV6   = "IPV6";
     String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6";
 
-    /* Deactivate data call reasons */
-    int DEACTIVATE_REASON_NONE = 0;
-    int DEACTIVATE_REASON_RADIO_OFF = 1;
-    int DEACTIVATE_REASON_PDP_RESET = 2;
-
     /* NV config radio reset types. */
     int NV_CONFIG_RELOAD_RESET = 1;
     int NV_CONFIG_ERASE_RESET = 2;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index ccf57b0..62fed61 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -83,28 +83,3 @@
         "junit",
     ],
 }
-
-// Build the legacy-android-test library
-// =====================================
-// This contains the android.test classes that were in Android API level 25,
-// including those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library_static {
-    name: "legacy-android-test",
-
-    srcs: [
-        "src/android/**/*.java",
-        "src/com/**/*.java",
-    ],
-
-    static_libs: [
-        "android.test.runner-minus-junit",
-        "android.test.mock",
-    ],
-
-    no_framework_libs: true,
-    libs: [
-        "framework",
-        "junit",
-    ],
-}
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
new file mode 100644
index 0000000..d2af8a9
--- /dev/null
+++ b/test-legacy/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Build the legacy-android-test library
+// =====================================
+// This contains the android.test classes that were in Android API level 25,
+// including those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library_static {
+    name: "legacy-android-test",
+
+    static_libs: [
+        "android.test.base-minus-junit",
+        "android.test.runner-minus-junit",
+        "android.test.mock",
+    ],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+        "junit",
+    ],
+}
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
new file mode 100644
index 0000000..b8c5326
--- /dev/null
+++ b/test-legacy/Android.mk
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
+# Build the android.test.legacy library
+# =====================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.legacy
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_JAVA_LIBRARIES := junit
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android.test.base-minus-junit \
+    android.test.runner-minus-junit \
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.legacy.jar)
+
+endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 229a6ac..706f636 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -117,23 +117,5 @@
 
 endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
 
-# Build the android.test.legacy library
-# =====================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.legacy
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src/android)
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_JAVA_LIBRARIES := android.test.mock.stubs junit
-LOCAL_STATIC_JAVA_LIBRARIES := android.test.base-minus-junit
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.legacy.jar)
-
 # additionally, build unit tests in a separate .apk
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index ebf5f68..c8f96c9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -285,7 +285,8 @@
 
         <activity
             android:name="ColoredShadowsActivity"
-            android:label="View/ColoredShadows">
+            android:label="View/ColoredShadows"
+            android:theme="@style/ThemeColoredShadows">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index 108709b..fa5437f 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -34,4 +34,11 @@
         <item name="android:translationZ">400dp</item>
         <item name="android:layout_alignParentBottom">true</item>
     </style>
+
+    <style name="ThemeColoredShadows" parent="@android:style/Theme.Material.Light">
+        <!--
+        <item name="android:ambientShadowAlpha">0</item>
+        <item name="android:spotShadowAlpha">1</item>
+        -->
+    </style>
 </resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
index 135c93c..901d90e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
@@ -17,6 +17,9 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,7 +39,9 @@
     private void setShadowColors(ViewGroup row, int rowIndex) {
         for (int i = 0; i < row.getChildCount(); i++) {
             View view = row.getChildAt(i);
-            view.setShadowColor(shadowColorFor(view));
+            //view.setBackground(new MyHackyBackground());
+            view.setOutlineSpotShadowColor(shadowColorFor(view));
+            view.setOutlineAmbientShadowColor(shadowColorFor(view));
             view.setElevation(6.0f * (rowIndex + 1));
         }
     }
@@ -44,12 +49,27 @@
     private int shadowColorFor(View view) {
         switch (view.getId()) {
             case R.id.grey: return 0xFF3C4043;
-            case R.id.blue: return 0xFF185ABC;
-            case R.id.red: return 0xFFB31412;
-            case R.id.yellow: return 0xFFEA8600;
-            case R.id.green: return 0xFF137333;
+            case R.id.blue: return Color.BLUE;
+            case R.id.red: return 0xFFEA4335;
+            case R.id.yellow: return 0xFFFBBC04;
+            case R.id.green: return 0xFF34A853;
             default: return 0xFF000000;
         }
     }
 
+    private static class MyHackyBackground extends ColorDrawable {
+        MyHackyBackground() {
+            super(0);
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public int getAlpha() {
+            return 254;
+        }
+    }
 }
diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk
new file mode 100644
index 0000000..a04f32a
--- /dev/null
+++ b/tests/UsbTests/Android.mk
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    frameworks-base-testutils \
+    android-support-test \
+    mockito-target-inline-minus-junit4 \
+    platform-test-annotations \
+    services.core \
+    services.net \
+    services.usb \
+    truth-prebuilt \
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsbTests
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsbTests/AndroidManifest.xml b/tests/UsbTests/AndroidManifest.xml
new file mode 100644
index 0000000..5d60695
--- /dev/null
+++ b/tests/UsbTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.usb" >
+
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.usb"
+                     android:label="UsbTests"/>
+</manifest>
diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml
new file mode 100644
index 0000000..0b623fb
--- /dev/null
+++ b/tests/UsbTests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs sample instrumentation test.">
+    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="UsbTests.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="UsbTests"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.usb"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
new file mode 100644
index 0000000..c491b46
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.hardware.usb.UsbManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.FgThread;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Tests for UsbHandler state changes.
+ */
+@RunWith(AndroidJUnit4.class)
+public class UsbHandlerTest {
+    private static final String TAG = UsbHandlerTest.class.getSimpleName();
+
+    @Mock
+    private UsbDeviceManager mUsbDeviceManager;
+    @Mock
+    private UsbDebuggingManager mUsbDebuggingManager;
+    @Mock
+    private UsbAlsaManager mUsbAlsaManager;
+    @Mock
+    private UsbSettingsManager mUsbSettingsManager;
+    @Mock
+    private SharedPreferences mSharedPreferences;
+    @Mock
+    private SharedPreferences.Editor mEditor;
+
+    private MockUsbHandler mUsbHandler;
+
+    private static final int MSG_UPDATE_STATE = 0;
+    private static final int MSG_ENABLE_ADB = 1;
+    private static final int MSG_SET_CURRENT_FUNCTIONS = 2;
+    private static final int MSG_SYSTEM_READY = 3;
+    private static final int MSG_BOOT_COMPLETED = 4;
+    private static final int MSG_USER_SWITCHED = 5;
+    private static final int MSG_UPDATE_USER_RESTRICTIONS = 6;
+    private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12;
+    private static final int MSG_UPDATE_SCREEN_LOCK = 13;
+
+    private Map<String, String> mMockProperties;
+    private Map<String, Integer> mMockGlobalSettings;
+
+    private class MockUsbHandler extends UsbDeviceManager.UsbHandler {
+        boolean mIsUsbTransferAllowed;
+        Intent mBroadcastedIntent;
+
+        MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
+                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+                UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
+            mUseUsbNotification = false;
+            mIsUsbTransferAllowed = true;
+            mCurrentUsbFunctionsReceived = true;
+        }
+
+        @Override
+        protected void setEnabledFunctions(long functions, boolean force) {
+            mCurrentFunctions = functions;
+        }
+
+        @Override
+        protected void setSystemProperty(String property, String value) {
+            mMockProperties.put(property, value);
+        }
+
+        @Override
+        protected void putGlobalSettings(ContentResolver resolver, String setting, int val) {
+            mMockGlobalSettings.put(setting, val);
+        }
+
+        @Override
+        protected String getSystemProperty(String property, String def) {
+            if (mMockProperties.containsKey(property)) {
+                return mMockProperties.get(property);
+            }
+            return def;
+        }
+
+        @Override
+        protected boolean isUsbTransferAllowed() {
+            return mIsUsbTransferAllowed;
+        }
+
+        @Override
+        protected SharedPreferences getPinnedSharedPrefs(Context context) {
+            return mSharedPreferences;
+        }
+
+        @Override
+        protected void sendStickyBroadcast(Intent intent) {
+            mBroadcastedIntent = intent;
+        }
+    }
+
+    @Before
+    public void before() {
+        MockitoAnnotations.initMocks(this);
+        mMockProperties = new HashMap<>();
+        mMockGlobalSettings = new HashMap<>();
+        when(mSharedPreferences.edit()).thenReturn(mEditor);
+
+        mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+                mUsbAlsaManager, mUsbSettingsManager);
+    }
+
+    @SmallTest
+    public void setFunctionsMtp() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+    }
+
+    @SmallTest
+    public void setFunctionsPtp() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_PTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_PTP, 0);
+    }
+
+    @SmallTest
+    public void setFunctionsMidi() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MIDI));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MIDI, 0);
+    }
+
+    @SmallTest
+    public void setFunctionsRndis() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_RNDIS));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_RNDIS, 0);
+    }
+
+    @SmallTest
+    public void enableAdb() {
+        sendBootCompleteMessages(mUsbHandler);
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 1));
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+        assertTrue(mUsbHandler.mAdbEnabled);
+        assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
+                .USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB);
+        verify(mUsbDebuggingManager).setAdbEnabled(true);
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
+
+        assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false));
+        assertTrue(mUsbHandler.mBroadcastedIntent
+                .getBooleanExtra(UsbManager.USB_CONFIGURED, false));
+        assertTrue(mUsbHandler.mBroadcastedIntent
+                .getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false));
+    }
+
+    @SmallTest
+    public void disableAdb() {
+        mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY,
+                UsbManager.USB_FUNCTION_ADB);
+        mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+                mUsbAlsaManager, mUsbSettingsManager);
+
+        sendBootCompleteMessages(mUsbHandler);
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0));
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+        assertFalse(mUsbHandler.mAdbEnabled);
+        assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
+                .USB_PERSISTENT_CONFIG_PROPERTY), "");
+        verify(mUsbDebuggingManager).setAdbEnabled(false);
+    }
+
+    @SmallTest
+    public void bootCompletedCharging() {
+        sendBootCompleteMessages(mUsbHandler);
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+    }
+
+    @Test
+    @SmallTest
+    public void bootCompletedAdbEnabled() {
+        mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb");
+        mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+                mUsbAlsaManager, mUsbSettingsManager);
+
+        sendBootCompleteMessages(mUsbHandler);
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+        assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1);
+        assertTrue(mUsbHandler.mAdbEnabled);
+        verify(mUsbDebuggingManager).setAdbEnabled(true);
+    }
+
+    @SmallTest
+    public void userSwitchedDisablesMtp() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_USER_SWITCHED,
+                UserHandle.getCallingUserId() + 1));
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+    }
+
+    @SmallTest
+    public void changedRestrictionsDisablesMtp() {
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+        mUsbHandler.mIsUsbTransferAllowed = false;
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_USER_RESTRICTIONS));
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+    }
+
+    @SmallTest
+    public void disconnectResetsCharging() {
+        sendBootCompleteMessages(mUsbHandler);
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 0, 0));
+
+        assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+    }
+
+    @SmallTest
+    public void configuredSendsBroadcast() {
+        sendBootCompleteMessages(mUsbHandler);
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
+
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+        assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false));
+        assertTrue(mUsbHandler.mBroadcastedIntent
+                .getBooleanExtra(UsbManager.USB_CONFIGURED, false));
+        assertTrue(mUsbHandler.mBroadcastedIntent
+                .getBooleanExtra(UsbManager.USB_FUNCTION_MTP, false));
+    }
+
+    @SmallTest
+    public void setScreenUnlockedFunctions() {
+        sendBootCompleteMessages(mUsbHandler);
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
+
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS,
+                UsbManager.FUNCTION_MTP));
+        assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0);
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+        verify(mEditor).putString(String.format(Locale.ENGLISH,
+                UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser),
+                UsbManager.USB_FUNCTION_MTP);
+    }
+
+    @SmallTest
+    public void unlockScreen() {
+        when(mSharedPreferences.getString(String.format(Locale.ENGLISH,
+                UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), ""))
+                .thenReturn(UsbManager.USB_FUNCTION_MTP);
+        sendBootCompleteMessages(mUsbHandler);
+        mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
+
+        assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0);
+        assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+    }
+
+    private static void sendBootCompleteMessages(Handler handler) {
+        handler.handleMessage(handler.obtainMessage(MSG_BOOT_COMPLETED));
+        handler.handleMessage(handler.obtainMessage(MSG_SYSTEM_READY));
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd4..e692652 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -311,7 +311,7 @@
         // Emulate pressing the USB tethering button in Settings UI.
         mTethering.startTethering(TETHERING_USB, null, false);
         mLooper.dispatchAll();
-        verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
 
         // Pretend we receive a USB connected broadcast. Here we also pretend
         // that the RNDIS function is somehow enabled, so that we see if we
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 069360e..df483b2 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -318,7 +318,7 @@
     float dimension_value = 4;
     float fraction_value = 5;
     int32 int_decimal_value = 6;
-    uint32 int_hexidecimal_value = 7;
+    uint32 int_hexadecimal_value = 7;
     bool boolean_value = 8;
     uint32 color_argb8_value = 9;
     uint32 color_rgb8_value = 10;
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 81bc2c8..f1eb952 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -792,9 +792,9 @@
           val.dataType = android::Res_value::TYPE_INT_DEC;
           val.data = static_cast<uint32_t>(pb_prim.int_decimal_value());
         } break;
-        case pb::Primitive::kIntHexidecimalValue: {
+        case pb::Primitive::kIntHexadecimalValue: {
           val.dataType = android::Res_value::TYPE_INT_HEX;
-          val.data = pb_prim.int_hexidecimal_value();
+          val.data = pb_prim.int_hexadecimal_value();
         } break;
         case pb::Primitive::kBooleanValue: {
           val.dataType = android::Res_value::TYPE_INT_BOOLEAN;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index e9622f5..1d00852 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -460,7 +460,7 @@
         pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
       } break;
       case android::Res_value::TYPE_INT_HEX: {
-        pb_prim->set_int_hexidecimal_value(val.data);
+        pb_prim->set_int_hexadecimal_value(val.data);
       } break;
       case android::Res_value::TYPE_INT_BOOLEAN: {
         pb_prim->set_boolean_value(static_cast<bool>(val.data));
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0c..fe63aa1 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,22 +7,21 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
 import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import java.util.List;
+
 /** @hide */
 @SystemApi
 @SystemService(Context.WIFI_RTT_SERVICE)
@@ -175,7 +174,8 @@
     @Deprecated
     @SuppressLint("Doclava125")
     public Capabilities getCapabilities() {
-        return new Capabilities();
+        throw new UnsupportedOperationException(
+                "getCapabilities is not supported in the adaptation layer");
     }
 
     /**
@@ -316,16 +316,7 @@
 
     @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
     public RttCapabilities getRttCapabilities() {
-        synchronized (mCapabilitiesLock) {
-            if (mRttCapabilities == null) {
-                try {
-                    mRttCapabilities = mService.getRttCapabilities();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-            return mRttCapabilities;
-        }
+        return mRttCapabilities;
     }
 
     /** specifies parameters for RTT request */
@@ -972,69 +963,6 @@
         }
     }
 
-    private boolean rttParamSanity(RttParams params, int index) {
-        if (mRttCapabilities == null) {
-            if(getRttCapabilities() == null) {
-                Log.e(TAG, "Can not get RTT capabilities");
-                throw new IllegalStateException("RTT chip is not working");
-            }
-        }
-
-        if (params.deviceType != RTT_PEER_TYPE_AP) {
-            return false;
-        } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
-                RTT_TYPE_TWO_SIDED) {
-            Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
-            return false;
-        } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
-                !mRttCapabilities.oneSidedRttSupported) {
-            Log.e(TAG, "Request " + index + ": One side RTT is not supported");
-            return false;
-        } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
-                !mRttCapabilities.twoSided11McRttSupported) {
-            Log.e(TAG, "Request " + index + ": two side RTT is not supported");
-            return false;
-        }  else if(params.bssid == null || params.bssid.isEmpty()) {
-            Log.e(TAG,"No BSSID in params");
-            return false;
-        } else if ( params.numberBurst != 0 ) {
-            Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
-            return false;
-        } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
-            Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
-                    params.numSamplesPerBurst);
-            return false;
-        } else if (params.numRetriesPerMeasurementFrame < 0 ||
-                params.numRetriesPerMeasurementFrame > 3) {
-            Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
-                    params.numRetriesPerMeasurementFrame);
-            return false;
-        } else if(params.numRetriesPerFTMR < 0 ||
-                params.numRetriesPerFTMR > 3) {
-            Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
-                    params.numRetriesPerFTMR);
-            return false;
-        } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
-            Log.e(TAG, "Request " + index + ": LCI is not supported");
-            return false;
-        } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
-            Log.e(TAG, "Request " + index + ": LCR is not supported");
-            return false;
-        } else if (params.burstTimeout < 1 ||
-                (params.burstTimeout > 11 && params.burstTimeout != 15)){
-            Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
-            return false;
-        } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
-            Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
-            return false;
-        } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
-            Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
-            return false;
-        }
-
-        return true;
-    }
-
     /**
      * Request to start an RTT ranging
      *
@@ -1045,24 +973,72 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void startRanging(RttParams[] params, RttListener listener) {
-        int index  = 0;
-        for(RttParams rttParam : params) {
-            if (!rttParamSanity(rttParam, index)) {
-                throw new IllegalArgumentException("RTT Request Parameter Illegal");
-            }
-            index++;
-        }
-        validateChannel();
-        ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         Log.i(TAG, "Send RTT request to RTT Service");
-        mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
-                0, putListener(listener), parcelableParams);
+
+        if (!mNewService.isAvailable()) {
+            listener.onFailure(REASON_NOT_AVAILABLE, "");
+            return;
+        }
+
+        RangingRequest.Builder builder = new RangingRequest.Builder();
+        for (RttParams rttParams : params) {
+            if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+                listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+                return;
+            }
+
+            ScanResult reconstructed = new ScanResult();
+            reconstructed.BSSID = rttParams.bssid;
+            if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+                reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+            }
+            reconstructed.channelWidth = rttParams.channelWidth;
+            reconstructed.frequency = rttParams.frequency;
+            reconstructed.centerFreq0 = rttParams.centerFreq0;
+            reconstructed.centerFreq1 = rttParams.centerFreq1;
+            builder.addResponder(
+                    android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+        }
+        try {
+            mNewService.startRanging(builder.build(), new RangingResultCallback() {
+                @Override
+                public void onRangingFailure(int code) {
+                    int localCode = REASON_UNSPECIFIED;
+                    if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+                        localCode = REASON_NOT_AVAILABLE;
+                    }
+                    listener.onFailure(localCode, "");
+                }
+
+                @Override
+                public void onRangingResults(List<RangingResult> results) {
+                    RttResult[] legacyResults = new RttResult[results.size()];
+                    int i = 0;
+                    for (RangingResult result : results) {
+                        legacyResults[i] = new RttResult();
+                        legacyResults[i].status = result.getStatus();
+                        legacyResults[i].bssid = result.getMacAddress().toString();
+                        legacyResults[i].distance = result.getDistanceMm() / 10;
+                        legacyResults[i].distanceStandardDeviation =
+                                result.getDistanceStdDevMm() / 10;
+                        legacyResults[i].rssi = result.getRssi();
+                        legacyResults[i].ts = result.getRangingTimestampUs();
+                    }
+                    listener.onSuccess(legacyResults);
+                }
+            }, null);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "startRanging: invalid arguments - " + e);
+            listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+        } catch (SecurityException e) {
+            Log.e(TAG, "startRanging: security exception - " + e);
+            listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+        }
     }
 
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void stopRanging(RttListener listener) {
-        validateChannel();
-        mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+        Log.e(TAG, "stopRanging: unsupported operation - nop");
     }
 
     /**
@@ -1095,12 +1071,8 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void enableResponder(ResponderCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        validateChannel();
-        int key = putListenerIfAbsent(callback);
-        mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+        throw new UnsupportedOperationException(
+                "enableResponder is not supported in the adaptation layer");
     }
 
     /**
@@ -1115,16 +1087,8 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void disableResponder(ResponderCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        validateChannel();
-        int key = removeListener(callback);
-        if (key == INVALID_KEY) {
-            Log.e(TAG, "responder not enabled yet");
-            return;
-        }
-        mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+        throw new UnsupportedOperationException(
+                "disableResponder is not supported in the adaptation layer");
     }
 
     /**
@@ -1238,17 +1202,9 @@
     /** @hide */
     public static final int CMD_OP_REG_BINDER           = BASE + 9;
 
-    private static final int INVALID_KEY = 0;
-
     private final Context mContext;
-    private final IRttManager mService;
-    private final SparseArray mListenerMap = new SparseArray();
-    private final Object mListenerMapLock = new Object();
-    private final Object mCapabilitiesLock = new Object();
-
+    private final WifiRttManager mNewService;
     private RttCapabilities mRttCapabilities;
-    private int mListenerKey = 1;
-    private AsyncChannel mAsyncChannel;
 
     /**
      * Create a new WifiScanner instance.
@@ -1263,170 +1219,20 @@
      */
     public RttManager(Context context, IRttManager service, Looper looper) {
         mContext = context;
-        mService = service;
-        Messenger messenger = null;
-        int[] key = new int[1];
-        try {
-            Log.d(TAG, "Get the messenger from " + mService);
-            messenger = mService.getMessenger(new Binder(), key);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
 
-        if (messenger == null) {
-            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
-        }
+        boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_RTT);
 
-        mAsyncChannel = new AsyncChannel();
-
-        Handler handler = new ServiceHandler(looper);
-        mAsyncChannel.connectSync(mContext, handler, messenger);
-        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
-        // synchronously, which causes RttService to receive the wrong replyTo value.
-        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
-                new RttClient(context.getPackageName()));
-        mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+        mRttCapabilities = new RttCapabilities();
+        mRttCapabilities.oneSidedRttSupported = rttSupported;
+        mRttCapabilities.twoSided11McRttSupported = rttSupported;
+        mRttCapabilities.lciSupported = false;
+        mRttCapabilities.lcrSupported = false;
+        mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+        mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+        mRttCapabilities.responderSupported = false;
+        mRttCapabilities.secureRttSupported = false;
     }
-
-    private void validateChannel() {
-        if (mAsyncChannel == null) throw new IllegalStateException(
-                "No permission to access and change wifi or a bad initialization");
-    }
-
-    private int putListener(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        int key;
-        synchronized (mListenerMapLock) {
-            do {
-                key = mListenerKey++;
-            } while (key == INVALID_KEY);
-            mListenerMap.put(key, listener);
-        }
-        return key;
-    }
-
-    // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
-    private int putListenerIfAbsent(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        synchronized (mListenerMapLock) {
-            int key = getListenerKey(listener);
-            if (key != INVALID_KEY) {
-                return key;
-            }
-            do {
-                key = mListenerKey++;
-            } while (key == INVALID_KEY);
-            mListenerMap.put(key, listener);
-            return key;
-        }
-
-    }
-
-    private Object getListener(int key) {
-        if (key == INVALID_KEY) return null;
-        synchronized (mListenerMapLock) {
-            Object listener = mListenerMap.get(key);
-            return listener;
-        }
-    }
-
-    private int getListenerKey(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        synchronized (mListenerMapLock) {
-            int index = mListenerMap.indexOfValue(listener);
-            if (index == -1) {
-                return INVALID_KEY;
-            } else {
-                return mListenerMap.keyAt(index);
-            }
-        }
-    }
-
-    private Object removeListener(int key) {
-        if (key == INVALID_KEY) return null;
-        synchronized (mListenerMapLock) {
-            Object listener = mListenerMap.get(key);
-            mListenerMap.remove(key);
-            return listener;
-        }
-    }
-
-    private int removeListener(Object listener) {
-        int key = getListenerKey(listener);
-        if (key == INVALID_KEY) return key;
-        synchronized (mListenerMapLock) {
-            mListenerMap.remove(key);
-            return key;
-        }
-    }
-
-    private class ServiceHandler extends Handler {
-        ServiceHandler(Looper looper) {
-            super(looper);
-        }
-        @Override
-        public void handleMessage(Message msg) {
-            Log.i(TAG, "RTT manager get message: " + msg.what);
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
-                    return;
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                    Log.e(TAG, "Channel connection lost");
-                    // This will cause all further async API calls on the WifiManager
-                    // to fail and throw an exception
-                    mAsyncChannel = null;
-                    getLooper().quit();
-                    return;
-            }
-
-            Object listener = getListener(msg.arg2);
-            if (listener == null) {
-                Log.e(TAG, "invalid listener key = " + msg.arg2 );
-                return;
-            } else {
-                Log.i(TAG, "listener key = " + msg.arg2);
-            }
-
-            switch (msg.what) {
-                /* ActionListeners grouped together */
-                case CMD_OP_SUCCEEDED :
-                    reportSuccess(listener, msg);
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_FAILED :
-                    reportFailure(listener, msg);
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_ABORTED :
-                    ((RttListener) listener).onAborted();
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
-                    ResponderConfig config = (ResponderConfig) msg.obj;
-                    ((ResponderCallback) (listener)).onResponderEnabled(config);
-                    break;
-                case CMD_OP_ENALBE_RESPONDER_FAILED:
-                    ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
-                    removeListener(msg.arg2);
-                    break;
-                default:
-                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
-                    return;
-            }
-        }
-
-        void reportSuccess(Object listener, Message msg) {
-            RttListener rttListener = (RttListener) listener;
-            ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
-            ((RttListener) listener).onSuccess(parcelableResults.mResults);
-        }
-
-        void reportFailure(Object listener, Message msg) {
-            RttListener rttListener = (RttListener) listener;
-            Bundle bundle = (Bundle) msg.obj;
-            ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
-        }
-    }
-
 }
 
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 9f73622..699f54c 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -22,6 +22,8 @@
 import android.net.NetworkSpecifier;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import dalvik.system.CloseGuard;
 
 import java.lang.ref.WeakReference;
@@ -142,6 +144,34 @@
     }
 
     /**
+     * Access the client ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal client ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getClientId() {
+        return mClientId;
+    }
+
+    /**
+     * Access the discovery session ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal discovery session ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getSessionId() {
+        return mSessionId;
+    }
+
+    /**
      * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -246,8 +276,7 @@
      *                   or
      *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
      *                   On a RESPONDER this value is used to gate the acceptance of a connection
-     *                   request from only that peer. A RESPONDER may specify a {@code null} -
-     *                   indicating that it will accept connection requests from any device.
+     *                   request from only that peer.
      *
      * @return A {@link NetworkSpecifier} to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
@@ -255,7 +284,7 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+    public NetworkSpecifier createNetworkSpecifierOpen(@NonNull PeerHandle peerHandle) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
             return null;
@@ -295,8 +324,7 @@
      * byte[], java.util.List)} or
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
-     *                   from only that peer. A RESPONDER may specify a {@code null} - indicating
-     *                   that it will accept connection requests from any device.
+     *                   from only that peer.
      * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
      *                   the passphrase. Use the
      *                   {@link #createNetworkSpecifierOpen(PeerHandle)} API to
@@ -309,7 +337,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierPassphrase(
-            @Nullable PeerHandle peerHandle, @NonNull String passphrase) {
+            @NonNull PeerHandle peerHandle, @NonNull String passphrase) {
         if (!WifiAwareUtils.validatePassphrase(passphrase)) {
             throw new IllegalArgumentException("Passphrase must meet length requirements");
         }
@@ -354,8 +382,7 @@
      * byte[], java.util.List)} or
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
-     *                   from only that peer. A RESPONDER may specify a null - indicating that
-     *                   it will accept connection requests from any device.
+     *                   from only that peer.
      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
      *            encrypting the data-path. Use the
      *            {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a
@@ -371,7 +398,7 @@
      * @hide
      */
     @SystemApi
-    public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+    public NetworkSpecifier createNetworkSpecifierPmk(@NonNull PeerHandle peerHandle,
             @NonNull byte[] pmk) {
         if (!WifiAwareUtils.validatePmk(pmk)) {
             throw new IllegalArgumentException("PMK must 32 bytes");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 2f0c316..06a5c2e 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -406,7 +406,7 @@
 
     /** @hide */
     public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
-            PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
+            @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
                     + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
@@ -420,12 +420,9 @@
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
-            if (peerHandle == null) {
-                throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid peer handle (value of null) - not "
-                                + "permitted on INITIATOR");
-            }
+        if (peerHandle == null) {
+            throw new IllegalArgumentException(
+                    "createNetworkSpecifier: Invalid peer handle - cannot be null");
         }
 
         return new WifiAwareNetworkSpecifier(
@@ -443,7 +440,7 @@
 
     /** @hide */
     public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role,
-            @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
+            @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role
                     + ", pmk=" + ((pmk == null) ? "null" : "non-null")
@@ -456,11 +453,9 @@
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
-            if (peer == null) {
-                throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC "
-                        + "address - null not permitted on INITIATOR");
-            }
+        if (peer == null) {
+            throw new IllegalArgumentException(
+                    "createNetworkSpecifier: Invalid peer MAC - cannot be null");
         }
         if (peer != null && peer.length != 6) {
             throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index f26b9f5..3219653 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -25,6 +25,8 @@
 import android.os.Looper;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import dalvik.system.CloseGuard;
 
 import java.lang.ref.WeakReference;
@@ -97,6 +99,20 @@
     }
 
     /**
+     * Access the client ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal client ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getClientId() {
+        return mClientId;
+    }
+
+    /**
      * Issue a request to the Aware service to create a new Aware publish discovery session, using
      * the specified {@code publishConfig} configuration. The results of the publish operation
      * are routed to the callbacks of {@link DiscoverySessionCallback}:
@@ -207,8 +223,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a {@code null} - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      *
      * @return A {@link NetworkSpecifier} to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
@@ -217,7 +232,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierOpen(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) {
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
@@ -246,8 +261,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a {@code null} - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
      *                   the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to
      *                   specify an open (unencrypted) link.
@@ -259,7 +273,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierPassphrase(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer,
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer,
             @NonNull String passphrase) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
@@ -293,8 +307,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a null - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
      *            encrypting the data-path. Use the
      *            {@link #createNetworkSpecifierPassphrase(int, byte[], String)} to specify a
@@ -311,7 +324,7 @@
      */
     @SystemApi
     public NetworkSpecifier createNetworkSpecifierPmk(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) {
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer, @NonNull byte[] pmk) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index b0a048d..84e3ed9 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -1022,7 +1022,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientNullPmk() throws Exception {
-        executeNetworkSpecifierWithClient(true, null, null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, null, null);
     }
 
     /**
@@ -1030,7 +1030,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientIncorrectLengthPmk() throws Exception {
-        executeNetworkSpecifierWithClient(true, "012".getBytes(), null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, "012".getBytes(), null);
     }
 
     /**
@@ -1038,7 +1038,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientNullPassphrase() throws Exception {
-        executeNetworkSpecifierWithClient(false, null, null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, null);
     }
 
     /**
@@ -1046,7 +1046,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientShortPassphrase() throws Exception {
-        executeNetworkSpecifierWithClient(false, null, "012");
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, "012");
     }
 
     /**
@@ -1054,15 +1054,23 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientLongPassphrase() throws Exception {
-        executeNetworkSpecifierWithClient(false, null,
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
                 "0123456789012345678901234567890123456789012345678901234567890123456789");
     }
 
-    private void executeNetworkSpecifierWithClient(boolean doPmk, byte[] pmk, String passphrase)
-            throws Exception {
+    /**
+     * Validate that a null PeerHandle triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientNullPeer() throws Exception {
+        executeNetworkSpecifierWithClient(null, false, null,
+                "0123456789012345678901234567890123456789012345678901234567890123456789");
+    }
+
+    private void executeNetworkSpecifierWithClient(PeerHandle peerHandle, boolean doPmk, byte[] pmk,
+            String passphrase) throws Exception {
         final int clientId = 4565;
         final int sessionId = 123;
-        final PeerHandle peerHandle = new PeerHandle(123412);
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
@@ -1108,7 +1116,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierDirectNullPmk() throws Exception {
-        executeNetworkSpecifierDirect(true, null, null);
+        executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
+                null, null);
     }
 
     /**
@@ -1116,7 +1125,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierDirectIncorrectLengthPmk() throws Exception {
-        executeNetworkSpecifierDirect(true, "012".getBytes(), null);
+        executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false), true,
+                "012".getBytes(), null);
     }
 
     /**
@@ -1124,7 +1134,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierDirectNullPassphrase() throws Exception {
-        executeNetworkSpecifierDirect(false, null, null);
+        executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
+                false, null, null);
     }
 
     /**
@@ -1132,7 +1143,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierDirectShortPassphrase() throws Exception {
-        executeNetworkSpecifierDirect(false, null, "012");
+        executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
+                false, null, "012");
     }
 
     /**
@@ -1140,14 +1152,22 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierDirectLongPassphrase() throws Exception {
-        executeNetworkSpecifierDirect(false, null,
+        executeNetworkSpecifierDirect(HexEncoding.decode("000102030405".toCharArray(), false),
+                false, null,
                 "0123456789012345678901234567890123456789012345678901234567890123456789");
     }
 
-    private void executeNetworkSpecifierDirect(boolean doPmk, byte[] pmk, String passphrase)
-            throws Exception {
+    /**
+     * Validate that a null peer MAC triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierDirectNullPeer() throws Exception {
+        executeNetworkSpecifierDirect(null, false, null, null);
+    }
+
+    private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk,
+            String passphrase) throws Exception {
         final int clientId = 134;
-        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
         final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();