Merge "Wifi power stats proto"
diff --git a/Android.bp b/Android.bp
index d0bee28..129d676 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",
@@ -483,16 +487,19 @@
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
         "telephony/java/android/telephony/data/IDataService.aidl",
         "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
-        "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
-	"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsConfig.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
+        "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
+	    "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
+        "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
         "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
@@ -510,13 +517,10 @@
         "telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
-        "telephony/java/com/android/ims/internal/IImsRegistration.aidl",
-        "telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsService.aidl",
         "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
         "telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl",
-	"telephony/java/com/android/ims/internal/IImsSmsListener.aidl",
         "telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl",
         "telephony/java/com/android/ims/internal/IImsUt.aidl",
         "telephony/java/com/android/ims/internal/IImsUtListener.aidl",
@@ -643,8 +647,10 @@
         ],
     },
 
-    // See comment on framework-oahl-backward-compatibility module below
     exclude_srcs: [
+        // See comment on framework-atb-backward-compatibility module below
+        "core/java/android/content/pm/AndroidTestBaseUpdater.java",
+        // See comment on framework-oahl-backward-compatibility module below
         "core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
     ],
 
@@ -697,6 +703,18 @@
     ],
 }
 
+// A temporary build target that is conditionally included on the bootclasspath if
+// android.test.base library has been removed and which provides support for
+// maintaining backwards compatibility for APKs that target pre-P and depend on
+// android.test.base classes. This is used iff REMOVE_ATB_FROM_BCP=true is
+// specified on the build command line.
+java_library {
+    name: "framework-atb-backward-compatibility",
+    srcs: [
+        "core/java/android/content/pm/AndroidTestBaseUpdater.java",
+    ],
+}
+
 genrule {
     name: "framework-statslog-gen",
     tools: ["stats-log-api-gen"],
diff --git a/Android.mk b/Android.mk
index 35b5f92..d72e398 100644
--- a/Android.mk
+++ b/Android.mk
@@ -821,6 +821,8 @@
 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 =======================================
diff --git a/api/current.txt b/api/current.txt
index e774f07..2b85972 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6085,16 +6085,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 +6498,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 +6583,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 +6698,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 +7166,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 +7187,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 +7250,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);
@@ -10043,7 +10051,6 @@
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
     field public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096; // 0x1000
-    field public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048; // 0x800
     field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
     field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
     field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -11622,15 +11629,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 +12393,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();
@@ -13049,29 +13050,29 @@
     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 +13080,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 +13156,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;
@@ -14215,22 +14216,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 +14245,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 +15814,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 +16176,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 +16269,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 +16363,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 +16381,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 +16409,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 +16726,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 +16739,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 +16791,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 +16801,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 +17097,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 +17904,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 +18332,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 +19566,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 +20539,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
@@ -22820,40 +22815,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 +22942,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 +22965,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 +23225,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 +23237,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 +23256,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 {
@@ -23317,18 +23312,21 @@
     method public java.lang.String getPropertyString(java.lang.String);
     method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
     method public byte[] getSecureStop(byte[]);
+    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;
     method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
     method public deprecated void release();
-    method public void releaseAllSecureStops();
+    method public deprecated void releaseAllSecureStops();
     method public void releaseSecureStops(byte[]);
+    method public void removeAllSecureStops();
     method public void removeKeys(byte[]);
+    method public void removeSecureStop(byte[]);
     method public void restoreKeys(byte[], byte[]);
     method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
     method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler);
@@ -23479,21 +23477,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
@@ -23516,22 +23514,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
@@ -24430,14 +24428,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);
@@ -25392,7 +25390,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;
   }
 
@@ -25417,7 +25415,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);
   }
@@ -25692,7 +25690,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();
@@ -25742,7 +25740,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";
@@ -26267,18 +26265,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
@@ -26289,16 +26287,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 {
@@ -26529,34 +26527,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 {
@@ -26586,11 +26584,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();
   }
 
 }
@@ -27023,10 +27021,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";
   }
@@ -32053,9 +32051,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();
@@ -32464,114 +32462,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;
   }
 
@@ -34225,7 +34223,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;
   }
 
@@ -34354,7 +34352,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;
   }
 
@@ -34447,8 +34445,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;
@@ -34463,7 +34461,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;
   }
 
@@ -34572,7 +34570,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;
@@ -34722,7 +34720,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";
@@ -34779,8 +34777,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";
@@ -34920,8 +34918,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";
@@ -34941,7 +34939,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";
@@ -34972,10 +34970,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";
@@ -35020,8 +35018,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";
@@ -35039,8 +35037,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";
@@ -35085,8 +35083,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";
@@ -35109,8 +35107,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";
@@ -35140,8 +35138,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";
@@ -35968,7 +35966,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 {
@@ -36004,7 +36002,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";
@@ -36040,8 +36038,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";
@@ -36064,7 +36062,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 {
@@ -36098,13 +36096,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;
@@ -36149,7 +36147,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";
   }
 
@@ -36381,12 +36379,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";
@@ -38189,6 +38187,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);
@@ -38296,9 +38325,9 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isTrustedUserPresenceRequired();
-    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public static final class KeyGenParameterSpec.Builder {
@@ -38323,10 +38352,10 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
-    method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
     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 {
@@ -38348,6 +38377,7 @@
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -38413,10 +38443,9 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
-    method public boolean isTrustedUserPresenceRequired();
-    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
+    method public boolean isUserConfirmationRequired();
   }
 
   public static final class KeyProtection.Builder {
@@ -38432,11 +38461,10 @@
     method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
-    method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
-    method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
     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 {
@@ -38534,6 +38562,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();
   }
@@ -38703,6 +38745,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();
@@ -38712,7 +38755,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);
@@ -40373,12 +40416,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();
@@ -40947,23 +40990,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 {
@@ -40992,10 +41035,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();
@@ -42448,11 +42491,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
@@ -44324,6 +44367,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
@@ -46506,76 +46554,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);
@@ -46583,13 +46631,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
@@ -48307,9 +48355,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);
@@ -49564,6 +49612,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();
@@ -50019,7 +50068,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();
@@ -50033,6 +50083,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 {
@@ -50073,6 +50125,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);
@@ -50102,32 +50155,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();
@@ -50138,13 +50201,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 {
@@ -50165,6 +50237,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 {
@@ -50477,7 +50618,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);
@@ -52289,6 +52430,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 {
@@ -55545,7 +55687,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;
@@ -55772,7 +55914,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();
@@ -57435,6 +57577,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> {
@@ -58587,8 +58730,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();
@@ -58601,7 +58744,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();
@@ -63257,13 +63400,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
   }
 
@@ -64544,7 +64687,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();
@@ -64631,7 +64774,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();
@@ -64687,7 +64830,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();
@@ -64740,7 +64883,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();
@@ -64792,8 +64935,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);
@@ -66242,15 +66385,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();
@@ -66271,7 +66414,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;
@@ -70457,7 +70599,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 396ff1a..b9f17d2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29,6 +29,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";
@@ -310,10 +311,8 @@
     ctor public InstantAppResolverService();
     method public final void attachBaseContext(android.content.Context);
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public deprecated void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
-    method public void onGetInstantAppIntentFilter(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
-    method public deprecated void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
-    method public void onGetInstantAppResolveInfo(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method public void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method public void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
   }
 
   public static final class InstantAppResolverService.InstantAppResolutionCallback {
@@ -822,14 +821,12 @@
     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_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
-    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_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";
     field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
     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";
   }
 
   public class IntentFilter implements android.os.Parcelable {
@@ -872,7 +869,6 @@
     ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int);
     ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long, android.os.Bundle);
     ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>);
-    ctor public InstantAppResolveInfo(android.os.Bundle);
     method public int describeContents();
     method public byte[] getDigestBytes();
     method public int getDigestPrefix();
@@ -881,7 +877,6 @@
     method public long getLongVersionCode();
     method public java.lang.String getPackageName();
     method public deprecated int getVersionCode();
-    method public boolean shouldLetInstallerDecide();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
   }
@@ -893,7 +888,6 @@
     method public int[] getDigestPrefix();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo.InstantAppDigest> CREATOR;
-    field public static final android.content.pm.InstantAppResolveInfo.InstantAppDigest UNDEFINED;
   }
 
   public final class IntentFilterVerificationInfo implements android.os.Parcelable {
@@ -2679,10 +2673,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 +4458,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 +4657,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 {
@@ -5101,16 +5113,669 @@
 
 package android.telephony.ims {
 
+  public final class ImsCallForwardInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCondition();
+    method public java.lang.String getNumber();
+    method public int getServiceClass();
+    method public int getStatus();
+    method public int getTimeSeconds();
+    method public int getToA();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
+  }
+
+  public final class ImsCallProfile implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getCallExtra(java.lang.String);
+    method public java.lang.String getCallExtra(java.lang.String, java.lang.String);
+    method public boolean getCallExtraBoolean(java.lang.String);
+    method public boolean getCallExtraBoolean(java.lang.String, boolean);
+    method public int getCallExtraInt(java.lang.String);
+    method public int getCallExtraInt(java.lang.String, int);
+    method public android.os.Bundle getCallExtras();
+    method public int getCallType();
+    method public static int getCallTypeFromVideoState(int);
+    method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
+    method public int getRestrictCause();
+    method public int getServiceType();
+    method public static int getVideoStateFromCallType(int);
+    method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+    method public boolean isVideoCall();
+    method public boolean isVideoPaused();
+    method public static int presentationToOir(int);
+    method public void setCallExtra(java.lang.String, java.lang.String);
+    method public void setCallExtraBoolean(java.lang.String, boolean);
+    method public void setCallExtraInt(java.lang.String, int);
+    method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
+    method public void updateCallType(android.telephony.ims.ImsCallProfile);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2
+    field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3
+    field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0
+    field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1
+    field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3
+    field public static final int CALL_TYPE_VOICE = 2; // 0x2
+    field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1
+    field public static final int CALL_TYPE_VS = 8; // 0x8
+    field public static final int CALL_TYPE_VS_RX = 10; // 0xa
+    field public static final int CALL_TYPE_VS_TX = 9; // 0x9
+    field public static final int CALL_TYPE_VT = 4; // 0x4
+    field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7
+    field public static final int CALL_TYPE_VT_RX = 6; // 0x6
+    field public static final int CALL_TYPE_VT_TX = 5; // 0x5
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR;
+    field public static final int DIALSTRING_NORMAL = 0; // 0x0
+    field public static final int DIALSTRING_SS_CONF = 1; // 0x1
+    field public static final int DIALSTRING_USSD = 2; // 0x2
+    field public static final java.lang.String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+    field public static final java.lang.String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+    field public static final java.lang.String EXTRA_CHILD_NUMBER = "ChildNum";
+    field public static final java.lang.String EXTRA_CNA = "cna";
+    field public static final java.lang.String EXTRA_CNAP = "cnap";
+    field public static final java.lang.String EXTRA_CODEC = "Codec";
+    field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
+    field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
+    field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
+    field public static final java.lang.String EXTRA_OI = "oi";
+    field public static final java.lang.String EXTRA_OIR = "oir";
+    field public static final java.lang.String EXTRA_REMOTE_URI = "remote_uri";
+    field public static final java.lang.String EXTRA_USSD = "ussd";
+    field public static final int OIR_DEFAULT = 0; // 0x0
+    field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
+    field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
+    field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+    field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
+    field public static final int SERVICE_TYPE_NONE = 0; // 0x0
+    field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
+  }
+
+  public class ImsCallSessionListener {
+    method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+    method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+    method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
+    method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+    method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+    method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
+    method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
+    method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
+    method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionInviteParticipantsRequestDelivered();
+    method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionMayHandover(int, int);
+    method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
+    method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+    method public void callSessionMultipartyStateChanged(boolean);
+    method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile);
+    method public void callSessionRemoveParticipantsRequestDelivered();
+    method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
+    method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+    method public void callSessionRttMessageReceived(java.lang.String);
+    method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
+    method public void callSessionRttModifyResponseReceived(int);
+    method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
+    method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionTtyModeReceived(int);
+    method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
+    method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
+    method public void callSessionUssdMessageReceived(int, java.lang.String);
+  }
+
+  public final class ImsConferenceState implements android.os.Parcelable {
+    method public int describeContents();
+    method public static int getConnectionStateForStatus(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR;
+    field public static final java.lang.String DISPLAY_TEXT = "display-text";
+    field public static final java.lang.String ENDPOINT = "endpoint";
+    field public static final java.lang.String SIP_STATUS_CODE = "sipstatuscode";
+    field public static final java.lang.String STATUS = "status";
+    field public static final java.lang.String STATUS_ALERTING = "alerting";
+    field public static final java.lang.String STATUS_CONNECTED = "connected";
+    field public static final java.lang.String STATUS_CONNECT_FAIL = "connect-fail";
+    field public static final java.lang.String STATUS_DIALING_IN = "dialing-in";
+    field public static final java.lang.String STATUS_DIALING_OUT = "dialing-out";
+    field public static final java.lang.String STATUS_DISCONNECTED = "disconnected";
+    field public static final java.lang.String STATUS_DISCONNECTING = "disconnecting";
+    field public static final java.lang.String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+    field public static final java.lang.String STATUS_ON_HOLD = "on-hold";
+    field public static final java.lang.String STATUS_PENDING = "pending";
+    field public static final java.lang.String STATUS_SEND_ONLY = "sendonly";
+    field public static final java.lang.String STATUS_SEND_RECV = "sendrecv";
+    field public static final java.lang.String USER = "user";
+    field public final java.util.HashMap<java.lang.String, android.os.Bundle> mParticipants;
+  }
+
+  public final class ImsExternalCallState implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.net.Uri getAddress();
+    method public int getCallId();
+    method public int getCallState();
+    method public int getCallType();
+    method public boolean isCallHeld();
+    method public boolean isCallPullable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALL_STATE_CONFIRMED = 1; // 0x1
+    field public static final int CALL_STATE_TERMINATED = 2; // 0x2
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
+  }
+
+  public final class ImsReasonInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCode();
+    method public int getExtraCode();
+    method public java.lang.String getExtraMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
+    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
+    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
+    field public static final int CODE_CALL_BARRED = 240; // 0xf0
+    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
+    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
+    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
+    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
+    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
+    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
+    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
+    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
+    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
+    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
+    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
+    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
+    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
+    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
+    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
+    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
+    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
+    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
+    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
+    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
+    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
+    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
+    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
+    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
+    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
+    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
+    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
+    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
+    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
+    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
+    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
+    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
+    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
+    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
+    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
+    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
+    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
+    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
+    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
+    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
+    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
+    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
+    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
+    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
+    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
+    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
+    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
+    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
+    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
+    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
+    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
+    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
+    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
+    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
+    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
+    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
+    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
+    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
+    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
+    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
+    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
+    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
+    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
+    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
+    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
+    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
+    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
+    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
+    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
+    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
+    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
+    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
+    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
+    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
+    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
+    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
+    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
+    field public static final int CODE_SIP_BUSY = 338; // 0x152
+    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
+    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
+    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
+    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
+    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
+    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
+    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
+    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
+    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
+    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
+    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
+    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
+    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
+    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
+    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
+    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
+    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
+    field public static final int CODE_UNSPECIFIED = 0; // 0x0
+    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
+    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
+    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
+    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
+    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
+    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
+    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
+    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
+    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
+    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
+    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
+    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
+    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
+    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
+    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
+    field public static final java.lang.String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
+  }
+
   public class ImsService extends android.app.Service {
     ctor public ImsService();
+    method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
+    method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
+    method public void disableIms(int);
+    method public void enableIms(int);
+    method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
+    method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
+    method public void readyForFeatureCreation();
+  }
+
+  public final class ImsSsData implements android.os.Parcelable {
+    ctor public ImsSsData();
+    method public int describeContents();
+    method public boolean isTypeBarring();
+    method public boolean isTypeCf();
+    method public boolean isTypeClip();
+    method public boolean isTypeClir();
+    method public boolean isTypeColp();
+    method public boolean isTypeColr();
+    method public boolean isTypeCw();
+    method public boolean isTypeIcb();
+    method public boolean isTypeInterrogation();
+    method public boolean isTypeUnConditional();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
+    field public static final int SS_ACTIVATION = 0; // 0x0
+    field public static final int SS_ALL_BARRING = 18; // 0x12
+    field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
+    field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5
+    field public static final int SS_ALL_TELESEVICES = 1; // 0x1
+    field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0
+    field public static final int SS_BAIC = 16; // 0x10
+    field public static final int SS_BAIC_ROAMING = 17; // 0x11
+    field public static final int SS_BAOC = 13; // 0xd
+    field public static final int SS_BAOIC = 14; // 0xe
+    field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf
+    field public static final int SS_CFU = 0; // 0x0
+    field public static final int SS_CFUT = 6; // 0x6
+    field public static final int SS_CF_ALL = 4; // 0x4
+    field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5
+    field public static final int SS_CF_BUSY = 1; // 0x1
+    field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3
+    field public static final int SS_CF_NO_REPLY = 2; // 0x2
+    field public static final int SS_CLIP = 7; // 0x7
+    field public static final int SS_CLIR = 8; // 0x8
+    field public static final int SS_CNAP = 11; // 0xb
+    field public static final int SS_COLP = 9; // 0x9
+    field public static final int SS_COLR = 10; // 0xa
+    field public static final int SS_DEACTIVATION = 1; // 0x1
+    field public static final int SS_ERASURE = 4; // 0x4
+    field public static final int SS_INCOMING_BARRING = 20; // 0x14
+    field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16
+    field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15
+    field public static final int SS_INTERROGATION = 2; // 0x2
+    field public static final int SS_OUTGOING_BARRING = 19; // 0x13
+    field public static final int SS_REGISTRATION = 3; // 0x3
+    field public static final int SS_SMS_SERVICES = 4; // 0x4
+    field public static final int SS_TELEPHONY = 2; // 0x2
+    field public static final int SS_WAIT = 12; // 0xc
+  }
+
+  public final class ImsSsInfo implements android.os.Parcelable {
+    ctor public ImsSsInfo();
+    method public int describeContents();
+    method public java.lang.String getIcbNum();
+    method public int getStatus();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
+    field public static final int DISABLED = 0; // 0x0
+    field public static final int ENABLED = 1; // 0x1
+    field public static final int NOT_REGISTERED = -1; // 0xffffffff
+  }
+
+  public final class ImsStreamMediaProfile implements android.os.Parcelable {
+    method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
+    method public int describeContents();
+    method public int getAudioDirection();
+    method public int getAudioQuality();
+    method public int getRttMode();
+    method public int getVideoDirection();
+    method public int getVideoQuality();
+    method public boolean isRttCall();
+    method public void setRttMode(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
+    field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2
+    field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4
+    field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5
+    field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7
+    field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6
+    field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14
+    field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11
+    field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13
+    field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12
+    field public static final int AUDIO_QUALITY_G711A = 13; // 0xd
+    field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf
+    field public static final int AUDIO_QUALITY_G711U = 11; // 0xb
+    field public static final int AUDIO_QUALITY_G722 = 14; // 0xe
+    field public static final int AUDIO_QUALITY_G723 = 12; // 0xc
+    field public static final int AUDIO_QUALITY_G729 = 16; // 0x10
+    field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8
+    field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9
+    field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa
+    field public static final int AUDIO_QUALITY_NONE = 0; // 0x0
+    field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR;
+    field public static final int DIRECTION_INACTIVE = 0; // 0x0
+    field public static final int DIRECTION_INVALID = -1; // 0xffffffff
+    field public static final int DIRECTION_RECEIVE = 1; // 0x1
+    field public static final int DIRECTION_SEND = 2; // 0x2
+    field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3
+    field public static final int RTT_MODE_DISABLED = 0; // 0x0
+    field public static final int RTT_MODE_FULL = 1; // 0x1
+    field public static final int VIDEO_QUALITY_NONE = 0; // 0x0
+    field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1
+    field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2
+    field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4
+    field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8
+    field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10
+  }
+
+  public final class ImsSuppServiceNotification implements android.os.Parcelable {
+    ctor public ImsSuppServiceNotification(int, int, int, int, java.lang.String, java.lang.String[]);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR;
+    field public final int code;
+    field public final java.lang.String[] history;
+    field public final int index;
+    field public final int notificationType;
+    field public final java.lang.String number;
+    field public final int type;
+  }
+
+  public class ImsUtListener {
+    method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData);
+    method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]);
+    method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]);
+    method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]);
+    method public void onUtConfigurationQueried(int, android.os.Bundle);
+    method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo);
+    method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo);
+    method public void onUtConfigurationUpdated(int);
+  }
+
+  public abstract class ImsVideoCallProvider {
+    ctor public ImsVideoCallProvider();
+    method public void changeCallDataUsage(long);
+    method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
+    method public void changePeerDimensions(int, int);
+    method public void changeVideoQuality(int);
+    method public void handleCallSessionEvent(int);
+    method public abstract void onRequestCallDataUsage();
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void onSetCamera(java.lang.String);
+    method public void onSetCamera(java.lang.String, int);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(android.net.Uri);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
+    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
   }
 
 }
 
-package android.telephony.ims.internal.stub {
+package android.telephony.ims.feature {
 
-  public class SmsImplBase {
-    ctor public SmsImplBase();
+  public final class CapabilityChangeRequest implements android.os.Parcelable {
+    method public void addCapabilitiesToDisableForTech(int, int);
+    method public void addCapabilitiesToEnableForTech(int, int);
+    method public int describeContents();
+    method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
+    method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR;
+  }
+
+  public static class CapabilityChangeRequest.CapabilityPair {
+    ctor public CapabilityChangeRequest.CapabilityPair(int, int);
+    method public int getCapability();
+    method public int getRadioTech();
+  }
+
+  public abstract class ImsFeature {
+    ctor public ImsFeature();
+    method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public abstract void onFeatureReady();
+    method public abstract void onFeatureRemoved();
+    method public final void setFeatureState(int);
+    field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff
+    field public static final int CAPABILITY_SUCCESS = 0; // 0x0
+    field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0
+    field public static final int FEATURE_MMTEL = 1; // 0x1
+    field public static final int FEATURE_RCS = 2; // 0x2
+    field public static final int STATE_INITIALIZING = 1; // 0x1
+    field public static final int STATE_READY = 2; // 0x2
+    field public static final int STATE_UNAVAILABLE = 0; // 0x0
+  }
+
+  protected static class ImsFeature.CapabilityCallbackProxy {
+    method public void onChangeCapabilityConfigurationError(int, int, int);
+  }
+
+  public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
+    ctor public MmTelFeature();
+    method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
+    method public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(android.telephony.ims.ImsCallProfile);
+    method public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
+    method public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
+    method public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
+    method public android.telephony.ims.stub.ImsUtImplBase getUt();
+    method public final void notifyCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+    method public final void notifyIncomingCall(android.telephony.ims.stub.ImsCallSessionImplBase, android.os.Bundle);
+    method public final void notifyVoiceMessageCountUpdate(int);
+    method public void onFeatureReady();
+    method public void onFeatureRemoved();
+    method public boolean queryCapabilityConfiguration(int, int);
+    method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
+    method public void setUiTtyMode(int, android.os.Message);
+    method public int shouldProcessCall(java.lang.String[]);
+    field public static final int PROCESS_CALL_CSFB = 1; // 0x1
+    field public static final int PROCESS_CALL_EMERGENCY_CSFB = 2; // 0x2
+    field public static final int PROCESS_CALL_IMS = 0; // 0x0
+  }
+
+  public static class MmTelFeature.MmTelCapabilities {
+    ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+    ctor public MmTelFeature.MmTelCapabilities(int);
+    method public final void addCapabilities(int);
+    method public final boolean isCapable(int);
+    method public final void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
+    field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
+    field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
+    field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
+  }
+
+  public static abstract class MmTelFeature.MmTelCapabilities.MmTelCapability implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class MmTelFeature.ProcessCallResult implements java.lang.annotation.Annotation {
+  }
+
+  public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
+    ctor public RcsFeature();
+    method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public void onFeatureReady();
+    method public void onFeatureRemoved();
+  }
+
+}
+
+package android.telephony.ims.stub {
+
+  public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
+    ctor public ImsCallSessionImplBase();
+    method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
+    method public void close();
+    method public void extendToConference(java.lang.String[]);
+    method public java.lang.String getCallId();
+    method public android.telephony.ims.ImsCallProfile getCallProfile();
+    method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider();
+    method public android.telephony.ims.ImsCallProfile getLocalCallProfile();
+    method public java.lang.String getProperty(java.lang.String);
+    method public android.telephony.ims.ImsCallProfile getRemoteCallProfile();
+    method public int getState();
+    method public void hold(android.telephony.ims.ImsStreamMediaProfile);
+    method public void inviteParticipants(java.lang.String[]);
+    method public boolean isInCall();
+    method public boolean isMultiparty();
+    method public void merge();
+    method public void reject(int);
+    method public void removeParticipants(java.lang.String[]);
+    method public void resume(android.telephony.ims.ImsStreamMediaProfile);
+    method public void sendDtmf(char, android.os.Message);
+    method public void sendRttMessage(java.lang.String);
+    method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
+    method public void sendRttModifyResponse(boolean);
+    method public void sendUssd(java.lang.String);
+    method public void setListener(android.telephony.ims.ImsCallSessionListener);
+    method public void setMute(boolean);
+    method public void start(java.lang.String, android.telephony.ims.ImsCallProfile);
+    method public void startConference(java.lang.String[], android.telephony.ims.ImsCallProfile);
+    method public void startDtmf(char);
+    method public void stopDtmf();
+    method public void terminate(int);
+    method public void update(int, android.telephony.ims.ImsStreamMediaProfile);
+    field public static final int USSD_MODE_NOTIFY = 0; // 0x0
+    field public static final int USSD_MODE_REQUEST = 1; // 0x1
+  }
+
+  public static class ImsCallSessionImplBase.State {
+    method public static java.lang.String toString(int);
+    field public static final int ESTABLISHED = 4; // 0x4
+    field public static final int ESTABLISHING = 3; // 0x3
+    field public static final int IDLE = 0; // 0x0
+    field public static final int INITIATED = 1; // 0x1
+    field public static final int INVALID = -1; // 0xffffffff
+    field public static final int NEGOTIATING = 2; // 0x2
+    field public static final int REESTABLISHING = 6; // 0x6
+    field public static final int RENEGOTIATING = 5; // 0x5
+    field public static final int TERMINATED = 8; // 0x8
+    field public static final int TERMINATING = 7; // 0x7
+  }
+
+  public class ImsConfigImplBase {
+    ctor public ImsConfigImplBase();
+    method public int getConfigInt(int);
+    method public java.lang.String getConfigString(int);
+    method public final void notifyProvisionedValueChanged(int, int);
+    method public final void notifyProvisionedValueChanged(int, java.lang.String);
+    method public int setConfig(int, int);
+    method public int setConfig(int, java.lang.String);
+    field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
+    field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
+    field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public class ImsEcbmImplBase {
+    ctor public ImsEcbmImplBase();
+    method public final void enteredEcbm();
+    method public void exitEmergencyCallbackMode();
+    method public final void exitedEcbm();
+  }
+
+  public final class ImsFeatureConfiguration implements android.os.Parcelable {
+    ctor public ImsFeatureConfiguration();
+    method public int describeContents();
+    method public int[] getServiceFeatures();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
+  }
+
+  public static class ImsFeatureConfiguration.Builder {
+    ctor public ImsFeatureConfiguration.Builder();
+    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int);
+    method public android.telephony.ims.stub.ImsFeatureConfiguration build();
+  }
+
+  public class ImsMultiEndpointImplBase {
+    ctor public ImsMultiEndpointImplBase();
+    method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
+    method public void requestImsExternalCallStateInfo();
+  }
+
+  public class ImsRegistrationImplBase {
+    ctor public ImsRegistrationImplBase();
+    method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
+    method public final void onRegistered(int);
+    method public final void onRegistering(int);
+    method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
+    method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+    field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+    field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+    field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+  }
+
+  public class ImsSmsImplBase {
+    ctor public ImsSmsImplBase();
     method public void acknowledgeSms(int, int, int);
     method public void acknowledgeSmsReport(int, int, int);
     method public java.lang.String getSmsFormat();
@@ -5129,6 +5794,29 @@
     field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1
   }
 
+  public class ImsUtImplBase {
+    ctor public ImsUtImplBase();
+    method public void close();
+    method public int queryCallBarring(int);
+    method public int queryCallBarringForServiceClass(int, int);
+    method public int queryCallForward(int, java.lang.String);
+    method public int queryCallWaiting();
+    method public int queryClip();
+    method public int queryClir();
+    method public int queryColp();
+    method public int queryColr();
+    method public void setListener(android.telephony.ims.ImsUtListener);
+    method public int transact(android.os.Bundle);
+    method public int updateCallBarring(int, int, java.lang.String[]);
+    method public int updateCallBarringForServiceClass(int, int, java.lang.String[], int);
+    method public int updateCallForward(int, int, java.lang.String, int, int);
+    method public int updateCallWaiting(boolean, int);
+    method public int updateClip(boolean);
+    method public int updateClir(int);
+    method public int updateColp(boolean);
+    method public int updateColr(int);
+  }
+
 }
 
 package android.telephony.mbms {
diff --git a/api/test-current.txt b/api/test-current.txt
index 4e8f904..bf00343 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -565,6 +565,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 +607,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 +995,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/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index d1af71d..54785ca 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -66,9 +66,12 @@
 namespace android {
 
 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
+static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
 static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
+static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
 static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
 static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
+static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
 static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
 
 static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
@@ -308,14 +311,20 @@
     bool encryptedAnimation = atoi(decrypt) != 0 ||
         !strcmp("trigger_restart_min_framework", decrypt);
 
-    if (!mShuttingDown && encryptedAnimation &&
-        (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
-        mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
-        return NO_ERROR;
+    if (!mShuttingDown && encryptedAnimation) {
+        static const char* encryptedBootFiles[] =
+            {PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE};
+        for (const char* f : encryptedBootFiles) {
+            if (access(f, R_OK) == 0) {
+                mZipFileName = f;
+                return NO_ERROR;
+            }
+        }
     }
-    static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
+    static const char* bootFiles[] =
+        {PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
     static const char* shutdownFiles[] =
-        {OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
+        {PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
 
     for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
         if (access(f, R_OK) == 0) {
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 565b092..eabbb96 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -137,7 +137,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 288ebe9..857a6dd 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -69,18 +69,17 @@
 
 using std::string;
 
-
 string HashableDimensionKey::toString() const {
     string flattened;
     DimensionsValueToString(getDimensionsValue(), &flattened);
     return flattened;
 }
 
-bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
     if (s1.field() != s2.field()) {
         return false;
     }
-    if (s1.value_case() != s1.value_case()) {
+    if (s1.value_case() != s2.value_case()) {
         return false;
     }
     switch (s1.value_case()) {
@@ -102,8 +101,8 @@
                 }
                 bool allMatched = true;
                 for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
-                    allMatched &= compareDimensionsValue(s1.value_tuple().dimensions_value(i),
-                                                    s2.value_tuple().dimensions_value(i));
+                    allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i),
+                                           s2.value_tuple().dimensions_value(i));
                 }
                 return allMatched;
             }
@@ -113,12 +112,54 @@
     }
 }
 
+bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
+    if (s1.field() != s2.field()) {
+        return s1.field() < s2.field();
+    }
+    if (s1.value_case() != s2.value_case()) {
+        return s1.value_case() < s2.value_case();
+    }
+    switch (s1.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return s1.value_str() < s2.value_str();
+        case DimensionsValue::ValueCase::kValueInt:
+            return s1.value_int() < s2.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return s1.value_long() < s2.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return (int)s1.value_bool() < (int)s2.value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return s1.value_float() < s2.value_float();
+        case DimensionsValue::ValueCase::kValueTuple:
+            {
+                if (s1.value_tuple().dimensions_value_size() !=
+                        s2.value_tuple().dimensions_value_size()) {
+                    return s1.value_tuple().dimensions_value_size() <
+                        s2.value_tuple().dimensions_value_size();
+                }
+                for (int i = 0;  i < s1.value_tuple().dimensions_value_size(); ++i) {
+                    if (EqualsTo(s1.value_tuple().dimensions_value(i),
+                                 s2.value_tuple().dimensions_value(i))) {
+                        continue;
+                    } else {
+                        return LessThan(s1.value_tuple().dimensions_value(i),
+                                        s2.value_tuple().dimensions_value(i));
+                    }
+                }
+                return false;
+            }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+        default:
+            return false;
+    }
+}
+
 bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
-    return compareDimensionsValue(getDimensionsValue(), that.getDimensionsValue());
+    return EqualsTo(getDimensionsValue(), that.getDimensionsValue());
 };
 
 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
-    return toString().compare(that.toString()) < 0;
+    return LessThan(getDimensionsValue(), that.getDimensionsValue());
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 3e71b53..a4066aa 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -91,23 +91,22 @@
 }
 
 void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
-    std::vector<Field> uidFields;
+    std::set<Field, FieldCmp> uidFields;
     if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
         android::util::kAtomsWithAttributionChain.end()) {
-        findFields(
-            event->getFieldValueMap(),
-            buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY),
-            &uidFields);
+        FieldMatcher matcher;
+        buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY, &matcher);
+        findFields(event->getFieldValueMap(), matcher, &uidFields);
     } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
                android::util::kAtomsWithUidField.end()) {
-        findFields(
-            event->getFieldValueMap(),
-            buildSimpleAtomFieldMatcher(event->GetTagId(), 1 /* uid is always the 1st field. */),
-            &uidFields);
+        FieldMatcher matcher;
+        buildSimpleAtomFieldMatcher(
+            event->GetTagId(), 1 /* uid is always the 1st field. */, &matcher);
+        findFields(event->getFieldValueMap(), matcher, &uidFields);
     }
 
-    for (size_t i = 0; i < uidFields.size(); ++i) {
-        DimensionsValue* value = event->findFieldValueOrNull(uidFields[i]);
+    for (const auto& uidField : uidFields) {
+        DimensionsValue* value = event->findFieldValueOrNull(uidField);
         if (value != nullptr && value->value_case() == DimensionsValue::ValueCase::kValueInt) {
             const int uid = mUidMap->getHostUidOrSelf(value->value_int());
             value->set_value_int(uid);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b32af02..4e570a6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -93,6 +93,8 @@
         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.
     }
 
@@ -669,6 +671,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:
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 7a1bb0c..5cfc349 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -289,7 +289,8 @@
     }
 
     // outputKey is the output values. e.g, uid:1234
-    const std::vector<DimensionsValue> outputValues = getDimensionKeys(event, mOutputDimensions);
+    std::vector<DimensionsValue> outputValues;
+    getDimensionKeys(event, mOutputDimensions, &outputValues);
     if (outputValues.size() == 0) {
         // The original implementation would generate an empty string dimension hash when condition
         // is not sliced.
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index ddfb8d1..3b2d480 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -116,28 +116,30 @@
     }
 }
 
-void getFieldsFromFieldMatcher(const FieldMatcher& matcher, const Field& parentField,
-                       std::vector<Field> *allFields) {
-    Field newParent = parentField;
-    Field* leaf = getSingleLeaf(&newParent);
-    leaf->set_field(matcher.field());
+void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
+                               std::vector<Field> *allFields) {
     if (matcher.child_size() == 0) {
-        allFields->push_back(newParent);
+        allFields->push_back(*rootField);
         return;
     }
     for (int i = 0; i < matcher.child_size(); ++i) {
-        leaf->add_child();
-        getFieldsFromFieldMatcher(matcher.child(i), newParent, allFields);
+        Field* newLeafField = leafField->add_child();
+        newLeafField->set_field(matcher.child(i).field());
+        getFieldsFromFieldMatcher(matcher.child(i), rootField, newLeafField, allFields);
     }
 }
 
 void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) {
-    Field parentField;
-    getFieldsFromFieldMatcher(matcher, parentField, allFields);
+    if (!matcher.has_field()) {
+        return;
+    }
+    Field rootField;
+    rootField.set_field(matcher.field());
+    getFieldsFromFieldMatcher(matcher, &rootField, &rootField, allFields);
 }
 
 void flattenValueLeaves(const DimensionsValue& value,
-                        std::vector<DimensionsValue> *allLaves) {
+                        std::vector<const DimensionsValue*> *allLaves) {
     switch (value.value_case()) {
         case DimensionsValue::ValueCase::kValueStr:
         case DimensionsValue::ValueCase::kValueInt:
@@ -145,7 +147,7 @@
         case DimensionsValue::ValueCase::kValueBool:
         case DimensionsValue::ValueCase::kValueFloat:
         case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            allLaves->push_back(value);
+            allLaves->push_back(&value);
             break;
         case DimensionsValue::ValueCase::kValueTuple:
             for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
@@ -155,45 +157,44 @@
     }
 }
 
-std::vector<HashableDimensionKey> getDimensionKeysForCondition(
-    const LogEvent& event, const MetricConditionLink& link) {
+void getDimensionKeysForCondition(
+    const LogEvent& event, const MetricConditionLink& link,
+    std::vector<HashableDimensionKey> *hashableDimensionKeys) {
     std::vector<Field> whatFields;
     getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields);
     std::vector<Field> conditionFields;
     getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields);
 
-    std::vector<HashableDimensionKey> hashableDimensionKeys;
-
     // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and
     // directly construct the full condition value tree.
-    std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.fields_in_what());
+    std::vector<DimensionsValue> whatValues;
+    getDimensionKeys(event, link.fields_in_what(), &whatValues);
 
     for (size_t i = 0; i < whatValues.size(); ++i) {
-        std::vector<DimensionsValue> whatLeaves;
+        std::vector<const DimensionsValue*> whatLeaves;
         flattenValueLeaves(whatValues[i], &whatLeaves);
         if (whatLeaves.size() != whatFields.size() ||
             whatLeaves.size() != conditionFields.size()) {
             ALOGE("Dimensions between what and condition not equal.");
-            return hashableDimensionKeys;
+            return;
         }
         FieldValueMap conditionValueMap;
         for (size_t j = 0; j < whatLeaves.size(); ++j) {
-            if (!setFieldInLeafValueProto(conditionFields[j], &whatLeaves[j])) {
+            DimensionsValue* conditionValue = &conditionValueMap[conditionFields[j]];
+            *conditionValue = *whatLeaves[i];
+            if (!setFieldInLeafValueProto(conditionFields[j], conditionValue)) {
                 ALOGE("Not able to reset the field for condition leaf value.");
-                return hashableDimensionKeys;
+                return;
             }
-            conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j]));
         }
-        std::vector<DimensionsValue> conditionValues;
-        findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValues);
-        if (conditionValues.size() != 1) {
+        std::vector<DimensionsValue> conditionValueTrees;
+        findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValueTrees);
+        if (conditionValueTrees.size() != 1) {
             ALOGE("Not able to find unambiguous field value in condition atom.");
             continue;
         }
-        hashableDimensionKeys.push_back(HashableDimensionKey(conditionValues[0]));
+        hashableDimensionKeys->push_back(HashableDimensionKey(conditionValueTrees[0]));
     }
-
-    return hashableDimensionKeys;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 598027b..a7288be 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -40,8 +40,9 @@
                                             const LogicalOperation& operation,
                                             const std::vector<ConditionState>& conditionCache);
 
-std::vector<HashableDimensionKey> getDimensionKeysForCondition(
-        const LogEvent& event, const MetricConditionLink& link);
+void getDimensionKeysForCondition(
+        const LogEvent& event, const MetricConditionLink& link,
+        std::vector<HashableDimensionKey> *dimensionKeys);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index bb7a044..04445ca 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -38,7 +38,7 @@
     return *leafValue;
 }
 
-void appendLeafNodeToParent(const Field& field,
+void appendLeafNodeToTree(const Field& field,
                             const DimensionsValue& value,
                             DimensionsValue* parentValue) {
     if (field.child_size() <= 0) {
@@ -58,24 +58,24 @@
         parentValue->mutable_value_tuple()->add_dimensions_value();
         idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1;
     }
-    appendLeafNodeToParent(
+    appendLeafNodeToTree(
         field.child(0), value,
         parentValue->mutable_value_tuple()->mutable_dimensions_value(idx));
 }
 
-void addNodeToRootDimensionsValues(const Field& field,
-                                   const DimensionsValue& node,
-                                   std::vector<DimensionsValue>* rootValues) {
-    if (rootValues == nullptr) {
+void appendLeafNodeToTrees(const Field& field,
+                           const DimensionsValue& node,
+                           std::vector<DimensionsValue>* rootTrees) {
+    if (rootTrees == nullptr) {
         return;
     }
-    if (rootValues->empty()) {
-        DimensionsValue rootValue;
-        appendLeafNodeToParent(field, node, &rootValue);
-        rootValues->push_back(rootValue);
+    if (rootTrees->empty()) {
+        DimensionsValue tree;
+        appendLeafNodeToTree(field, node, &tree);
+        rootTrees->push_back(tree);
     } else {
-        for (size_t i = 0; i < rootValues->size(); ++i) {
-            appendLeafNodeToParent(field, node, &rootValues->at(i));
+        for (size_t i = 0; i < rootTrees->size(); ++i) {
+            appendLeafNodeToTree(field, node, &rootTrees->at(i));
         }
     }
 }
@@ -85,22 +85,25 @@
 void findDimensionsValues(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
+       Field* rootField,
+       Field* leafField,
        std::vector<DimensionsValue>* rootDimensionsValues);
 
 void findNonRepeatedDimensionsValues(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
+       Field* rootField,
+       Field* leafField,
        std::vector<DimensionsValue>* rootValues) {
     if (matcher.child_size() > 0) {
+        Field* newLeafField = leafField->add_child();
         for (const auto& childMatcher : matcher.child()) {
-          Field childField = field;
-          appendLeaf(&childField, childMatcher.field());
-          findDimensionsValues(fieldValueMap, childMatcher, childField, rootValues);
+          newLeafField->set_field(childMatcher.field());
+          findDimensionsValues(fieldValueMap, childMatcher, rootField, newLeafField, rootValues);
         }
+        leafField->clear_child();
     } else {
-        auto ret = fieldValueMap.equal_range(field);
+        auto ret = fieldValueMap.equal_range(*rootField);
         int found = 0;
         for (auto it = ret.first; it != ret.second; ++it) {
             found++;
@@ -113,40 +116,43 @@
             ALOGE("Found multiple values for optional field.");
             return;
         }
-        addNodeToRootDimensionsValues(field, ret.first->second, rootValues);
+        appendLeafNodeToTrees(*rootField, ret.first->second, rootValues);
     }
 }
 
 void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap,
                                   const FieldMatcher& matcher,
-                                  const Field& field,
+                                  Field* rootField,
+                                  Field* leafField,
                                   std::vector<DimensionsValue>* rootValues) {
     if (matcher.position() == Position::FIRST) {
-        Field first_field = field;
-        setPositionForLeaf(&first_field, 0);
-        findNonRepeatedDimensionsValues(fieldValueMap, matcher, first_field, rootValues);
+        leafField->set_position_index(0);
+        findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, rootValues);
+        leafField->clear_position_index();
     } else {
-        auto itLower = fieldValueMap.lower_bound(field);
+        auto itLower = fieldValueMap.lower_bound(*rootField);
         if (itLower == fieldValueMap.end()) {
             return;
         }
-        Field next_field = field;
-        getNextField(&next_field);
-        auto itUpper = fieldValueMap.lower_bound(next_field);
+        const int leafFieldNum = leafField->field();
+        leafField->set_field(leafFieldNum + 1);
+        auto itUpper = fieldValueMap.lower_bound(*rootField);
+        // Resets the field number.
+        leafField->set_field(leafFieldNum);
 
         switch (matcher.position()) {
              case Position::LAST:
                  {
                      itUpper--;
                      if (itUpper != fieldValueMap.end()) {
-                         Field last_field = field;
-                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
                          if (last_index < 0) {
                             return;
                          }
-                         setPositionForLeaf(&last_field, last_index);
+                         leafField->set_position_index(last_index);
                          findNonRepeatedDimensionsValues(
-                            fieldValueMap, matcher, last_field, rootValues);
+                            fieldValueMap, matcher, rootField, leafField, rootValues);
+                         leafField->clear_position_index();
                      }
                  }
                  break;
@@ -154,20 +160,20 @@
                  {
                     std::set<int> indexes;
                     for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(field, it->first);
+                        int index = getPositionByReferenceField(*rootField, it->first);
                         if (index >= 0) {
                             indexes.insert(index);
                         }
                     }
                     if (!indexes.empty()) {
-                        Field any_field = field;
                         std::vector<DimensionsValue> allValues;
                         for (const int index : indexes) {
-                             setPositionForLeaf(&any_field, index);
+                             leafField->set_position_index(index);
                              std::vector<DimensionsValue> newValues = *rootValues;
                              findNonRepeatedDimensionsValues(
-                                fieldValueMap, matcher, any_field, &newValues);
+                                fieldValueMap, matcher, rootField, leafField, &newValues);
                              allValues.insert(allValues.end(), newValues.begin(), newValues.end());
+                             leafField->clear_position_index();
                         }
                         rootValues->clear();
                         rootValues->insert(rootValues->end(), allValues.begin(), allValues.end());
@@ -183,12 +189,15 @@
 void findDimensionsValues(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
+       Field* rootField,
+       Field* leafField,
        std::vector<DimensionsValue>* rootDimensionsValues) {
     if (!matcher.has_position()) {
-        findNonRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+        findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField,
+                                        rootDimensionsValues);
     } else {
-        findRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+        findRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField,
+                                     rootDimensionsValues);
     }
 }
 
@@ -198,56 +207,49 @@
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
        std::vector<DimensionsValue>* rootDimensionsValues) {
-    findDimensionsValues(fieldValueMap, matcher,
-                    buildSimpleAtomField(matcher.field()), rootDimensionsValues);
+    Field rootField;
+    buildSimpleAtomField(matcher.field(), &rootField);
+    findDimensionsValues(fieldValueMap, matcher, &rootField, &rootField, rootDimensionsValues);
 }
 
-FieldMatcher buildSimpleAtomFieldMatcher(const int tagId) {
-    FieldMatcher matcher;
-    matcher.set_field(tagId);
-    return matcher;
+void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) {
+    matcher->set_field(tagId);
 }
 
-FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum) {
-    FieldMatcher matcher;
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(atomFieldNum);
-    return matcher;
+void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+    matcher->add_child()->set_field(fieldNum);
 }
 
 constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1;
 constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1;
 constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2;
 
-FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position) {
-    FieldMatcher matcher;
-    matcher.set_field(tagId);
-    auto child = matcher.add_child();
+void buildAttributionUidFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+    auto child = matcher->add_child();
     child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
     child->set_position(position);
     child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-    return matcher;
 }
 
-FieldMatcher buildAttributionTagFieldMatcher(const int tagId, const Position position) {
-    FieldMatcher matcher;
-    matcher.set_field(tagId);
-    FieldMatcher* child = matcher.add_child();
+void buildAttributionTagFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+    FieldMatcher* child = matcher->add_child();
     child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
     child->set_position(position);
     child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-    return matcher;
 }
 
-FieldMatcher buildAttributionFieldMatcher(const int tagId, const Position position) {
-    FieldMatcher matcher;
-    matcher.set_field(tagId);
-    FieldMatcher* child = matcher.add_child();
+void buildAttributionFieldMatcher(const int tagId, const Position position, FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+    FieldMatcher* child = matcher->add_child();
     child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
     child->set_position(position);
     child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
     child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-    return matcher;
 }
 
 void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
@@ -284,28 +286,6 @@
     }
 }
 
-void getDimensionsValueLeafNodes(
-    const DimensionsValue& value, std::vector<DimensionsValue> *leafNodes) {
-    switch (value.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::kValueInt:
-        case DimensionsValue::ValueCase::kValueLong:
-        case DimensionsValue::ValueCase::kValueBool:
-        case DimensionsValue::ValueCase::kValueFloat:
-            leafNodes->push_back(value);
-            break;
-        case DimensionsValue::ValueCase::kValueTuple:
-            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
-                getDimensionsValueLeafNodes(value.value_tuple().dimensions_value(i), leafNodes);
-            }
-            break;
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            break;
-        default:
-            break;
-    }
-}
-
 std::string DimensionsValueToString(const DimensionsValue& value) {
     std::string flatten;
     DimensionsValueToString(value, &flatten);
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index d0f96a2..e900c5e 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -33,8 +33,7 @@
 DimensionsValue getSingleLeafValue(const DimensionsValue& value);
 
 // Appends the leaf node to the parent tree.
-void appendLeafNodeToParent(const Field& field, const DimensionsValue& value,
-                            DimensionsValue* parentValue);
+void appendLeafNodeToTree(const Field& field, const DimensionsValue& value, DimensionsValue* tree);
 
 // Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
 // represents a tree. When the input proto has repeated fields and the input "dimensions" wants
@@ -45,13 +44,16 @@
        std::vector<DimensionsValue>* rootDimensionsValues);
 
 // Utils to build FieldMatcher proto for simple one-depth atoms.
-FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum);
-FieldMatcher buildSimpleAtomFieldMatcher(const int tagId);
+void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
+void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
 
 // Utils to build FieldMatcher proto for attribution nodes.
-FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position);
-FieldMatcher buildAttributionTagFieldMatcher(const int tagId, const Position position);
-FieldMatcher buildAttributionFieldMatcher(const int tagId, const Position position);
+void buildAttributionUidFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher* matcher);
+void buildAttributionTagFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher* matcher);
+void buildAttributionFieldMatcher(const int tagId, const Position position,
+                                  FieldMatcher* matcher);
 
 // Utils to print pretty string for DimensionsValue proto.
 std::string DimensionsValueToString(const DimensionsValue& value);
diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp
index 4ff4f74..acf64fe 100644
--- a/cmds/statsd/src/field_util.cpp
+++ b/cmds/statsd/src/field_util.cpp
@@ -102,24 +102,13 @@
     }
 }
 
-Field buildAtomField(const int tagId, const Field &atomField) {
-    Field field;
-    *field.add_child() = atomField;
-    field.set_field(tagId);
-    return field;
+void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field *field) {
+    field->set_field(tagId);
+    field->add_child()->set_field(atomFieldNum);
 }
 
-Field buildSimpleAtomField(const int tagId, const int atomFieldNum) {
-    Field field;
-    field.set_field(tagId);
-    field.add_child()->set_field(atomFieldNum);
-    return field;
-}
-
-Field buildSimpleAtomField(const int tagId) {
-    Field field;
-    field.set_field(tagId);
-    return field;
+void buildSimpleAtomField(const int tagId, Field *field) {
+    field->set_field(tagId);
 }
 
 void appendLeaf(Field *parent, int node_field_num) {
@@ -145,18 +134,6 @@
     }
 }
 
-
-void getNextField(Field* field) {
-    if (field->child_size() <= 0) {
-        field->set_field(field->field() + 1);
-        return;
-    }
-    if (field->child_size() != 1) {
-        return;
-    }
-    getNextField(field->mutable_child(0));
-}
-
 void increasePosition(Field *field) {
     if (!field->has_position_index()) {
         field->set_position_index(0);
@@ -176,34 +153,30 @@
     return getPositionByReferenceField(ref.child(0), field_with_index.child(0));
 }
 
-void setPositionForLeaf(Field *field, int index) {
-    if (field->child_size() <= 0) {
-        field->set_position_index(index);
-    } else {
-        setPositionForLeaf(field->mutable_child(0), index);
-    }
-}
-
 namespace {
+
 void findFields(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
-       std::vector<Field>* rootFields);
+       Field* rootField,
+       Field* leafField,
+       std::set<Field, FieldCmp>* rootFields);
 
 void findNonRepeatedFields(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
-       std::vector<Field>* rootFields) {
+       Field* rootField,
+       Field* leafField,
+       std::set<Field, FieldCmp>* rootFields) {
     if (matcher.child_size() > 0) {
+        Field* newLeafField = leafField->add_child();
         for (const auto& childMatcher : matcher.child()) {
-          Field childField = field;
-          appendLeaf(&childField, childMatcher.field());
-          findFields(fieldValueMap, childMatcher, childField, rootFields);
+            newLeafField->set_field(childMatcher.field());
+            findFields(fieldValueMap, childMatcher, rootField, newLeafField, rootFields);
         }
+        leafField->clear_child();
     } else {
-        auto ret = fieldValueMap.equal_range(field);
+        auto ret = fieldValueMap.equal_range(*rootField);
         int found = 0;
         for (auto it = ret.first; it != ret.second; ++it) {
             found++;
@@ -216,38 +189,42 @@
             ALOGE("Found multiple values for optional field.");
             return;
         }
-        rootFields->push_back(ret.first->first);
+        rootFields->insert(ret.first->first);
     }
 }
 
 void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
-                        const Field& field, std::vector<Field>* rootFields) {
+                        Field* rootField, Field* leafField,
+                        std::set<Field, FieldCmp>* rootFields) {
     if (matcher.position() == Position::FIRST) {
-        Field first_field = field;
-        setPositionForLeaf(&first_field, 0);
-        findNonRepeatedFields(fieldValueMap, matcher, first_field, rootFields);
+        leafField->set_position_index(0);
+        findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
+        leafField->clear_position_index();
     } else {
-        auto itLower = fieldValueMap.lower_bound(field);
+        auto itLower = fieldValueMap.lower_bound(*rootField);
         if (itLower == fieldValueMap.end()) {
             return;
         }
-        Field next_field = field;
-        getNextField(&next_field);
-        auto itUpper = fieldValueMap.lower_bound(next_field);
+
+        const int leafFieldNum = leafField->field();
+        leafField->set_field(leafFieldNum + 1);
+        auto itUpper = fieldValueMap.lower_bound(*rootField);
+        // Resets the field number.
+        leafField->set_field(leafFieldNum);
 
         switch (matcher.position()) {
              case Position::LAST:
                  {
                      itUpper--;
                      if (itUpper != fieldValueMap.end()) {
-                         Field last_field = field;
-                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
                          if (last_index < 0) {
                             return;
                          }
-                         setPositionForLeaf(&last_field, last_index);
+                         leafField->set_position_index(last_index);
                          findNonRepeatedFields(
-                            fieldValueMap, matcher, last_field, rootFields);
+                            fieldValueMap, matcher, rootField, leafField, rootFields);
+                         leafField->clear_position_index();
                      }
                  }
                  break;
@@ -255,17 +232,17 @@
                  {
                     std::set<int> indexes;
                     for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(field, it->first);
+                        int index = getPositionByReferenceField(*rootField, it->first);
                         if (index >= 0) {
                             indexes.insert(index);
                         }
                     }
                     if (!indexes.empty()) {
-                        Field any_field = field;
                         for (const int index : indexes) {
-                             setPositionForLeaf(&any_field, index);
+                             leafField->set_position_index(index);
                              findNonRepeatedFields(
-                                fieldValueMap, matcher, any_field, rootFields);
+                                fieldValueMap, matcher, rootField, leafField, rootFields);
+                             leafField->clear_position_index();
                         }
                     }
                  }
@@ -279,12 +256,13 @@
 void findFields(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       const Field& field,
-       std::vector<Field>* rootFields) {
+       Field* rootField,
+       Field* leafField,
+       std::set<Field, FieldCmp>* rootFields) {
     if (!matcher.has_position()) {
-        findNonRepeatedFields(fieldValueMap, matcher, field, rootFields);
+        findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
     } else {
-        findRepeatedFields(fieldValueMap, matcher, field, rootFields);
+        findRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
     }
 }
 
@@ -293,17 +271,24 @@
 void findFields(
        const FieldValueMap& fieldValueMap,
        const FieldMatcher& matcher,
-       std::vector<Field>* rootFields) {
-    return findFields(fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), rootFields);
+       std::set<Field, FieldCmp>* rootFields) {
+    if (!matcher.has_field() || fieldValueMap.empty()) {
+        return;
+    }
+    Field rootField;
+    buildSimpleAtomField(matcher.field(), &rootField);
+    return findFields(fieldValueMap, matcher, &rootField, &rootField, rootFields);
 }
 
 void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) {
-    std::vector<Field> rootFields;
+    if (!matcher.has_field()) {
+        return;
+    }
+    std::set<Field, FieldCmp> rootFields;
     findFields(*fieldValueMap, matcher, &rootFields);
-    std::set<Field, FieldCmp> rootFieldSet(rootFields.begin(), rootFields.end());
     auto it = fieldValueMap->begin();
     while (it != fieldValueMap->end()) {
-        if (rootFieldSet.find(it->first) == rootFieldSet.end()) {
+        if (rootFields.find(it->first) == rootFields.end()) {
             it = fieldValueMap->erase(it);
         } else {
             it++;
diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h
index a4dfddd..b04465d 100644
--- a/cmds/statsd/src/field_util.h
+++ b/cmds/statsd/src/field_util.h
@@ -20,7 +20,8 @@
 #include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
-#include <unordered_map>
+#include <map>
+#include <set>
 
 namespace android {
 namespace os {
@@ -54,15 +55,9 @@
 void appendLeaf(Field *parent, int node_field_num);
 void appendLeaf(Field *parent, int node_field_num, int position);
 
-// Given the field sorting logic, this function is to increase the "field" at the leaf node.
-void getNextField(Field* field);
-
 // Increase the position index for the node. If the "position_index" is not set, set it as 0.
 void increasePosition(Field *field);
 
-// Finds the leaf node and set the index there.
-void setPositionForLeaf(Field *field, int index);
-
 // Returns true if the matcher has specified at least one leaf node.
 bool hasLeafNode(const FieldMatcher& matcher);
 
@@ -72,15 +67,13 @@
 int getPositionByReferenceField(const Field& reference, const Field& field_with_index);
 
 // Utils to build the Field proto for simple atom fields.
-Field buildAtomField(const int tagId, const Field &atomField);
-Field buildSimpleAtomField(const int tagId, const int atomFieldNum);
-Field buildSimpleAtomField(const int tagId);
+void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field* field);
+void buildSimpleAtomField(const int tagId, Field* field);
 
 // Find out all the fields specified by the matcher.
 void findFields(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       std::vector<Field>* rootFields);
+       const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
+       std::set<Field, FieldCmp>* rootFields);
 
 // Filter out the fields not in the field matcher.
 void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1ca793c..9e72f5b 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -198,7 +198,8 @@
 
     int seenListStart = 0;
 
-    Field field;
+    Field fieldTree;
+    Field* atomField = fieldTree.add_child();
     do {
         elem = android_log_read_next(context);
         switch ((int)elem.type) {
@@ -206,51 +207,37 @@
                 // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id.
                 if (i == 1) {
                     mTagId = elem.data.int32;
+                    fieldTree.set_field(mTagId);
                 } else {
-                    increaseField(&field, seenListStart > 0/* is_child */);
-                    DimensionsValue dimensionsValue;
-                    dimensionsValue.set_value_int(elem.data.int32);
-                    setFieldInLeafValueProto(field, &dimensionsValue);
-                    mFieldValueMap.insert(
-                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                    increaseField(atomField, seenListStart > 0/* is_child */);
+                    mFieldValueMap[fieldTree].set_value_int(elem.data.int32);
                 }
                 break;
             case EVENT_TYPE_FLOAT:
                 {
-                    increaseField(&field, seenListStart > 0/* is_child */);
-                    DimensionsValue dimensionsValue;
-                    dimensionsValue.set_value_float(elem.data.float32);
-                    setFieldInLeafValueProto(field, &dimensionsValue);
-                    mFieldValueMap.insert(
-                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                    increaseField(atomField, seenListStart > 0/* is_child */);
+                    mFieldValueMap[fieldTree].set_value_float(elem.data.float32);
                 }
                 break;
             case EVENT_TYPE_STRING:
                 {
-                    increaseField(&field, seenListStart > 0/* is_child */);
-                    DimensionsValue dimensionsValue;
-                    dimensionsValue.set_value_str(string(elem.data.string, elem.len).c_str());
-                    setFieldInLeafValueProto(field, &dimensionsValue);
-                    mFieldValueMap.insert(
-                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                    increaseField(atomField, seenListStart > 0/* is_child */);
+                    mFieldValueMap[fieldTree].set_value_str(
+                        string(elem.data.string, elem.len).c_str());
                 }
                 break;
             case EVENT_TYPE_LONG:
                 {
-                    increaseField(&field, seenListStart > 0 /* is_child */);
-                    DimensionsValue dimensionsValue;
-                    dimensionsValue.set_value_long(elem.data.int64);
-                    setFieldInLeafValueProto(field, &dimensionsValue);
-                    mFieldValueMap.insert(
-                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                    increaseField(atomField, seenListStart > 0 /* is_child */);
+                    mFieldValueMap[fieldTree].set_value_long(elem.data.int64);
                 }
                 break;
             case EVENT_TYPE_LIST:
                 if (i >= 1) {
                     if (seenListStart > 0) {
-                       increasePosition(&field);
+                       increasePosition(atomField);
                     } else {
-                        increaseField(&field, false /* is_child */);
+                        increaseField(atomField, false /* is_child */);
                     }
                     seenListStart++;
                     if (seenListStart >= 3) {
@@ -262,10 +249,10 @@
             case EVENT_TYPE_LIST_STOP:
                 seenListStart--;
                 if (seenListStart == 0) {
-                    field.clear_position_index();
+                    atomField->clear_position_index();
                 } else {
-                    if (field.child_size() > 0) {
-                       field.mutable_child(0)->clear_field();
+                    if (atomField->child_size() > 0) {
+                       atomField->mutable_child(0)->clear_field();
                     }
                 }
                 break;
@@ -393,14 +380,9 @@
 
 bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField,
                                                  DimensionsValue* dimensionsValue) const {
-    return GetAtomDimensionsValueProto(
-        buildSimpleAtomFieldMatcher(mTagId, atomField), dimensionsValue);
-}
-
-DimensionsValue LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField)  const {
-    DimensionsValue dimensionsValue;
-    GetSimpleAtomDimensionsValueProto(atomField, &dimensionsValue);
-    return dimensionsValue;
+    FieldMatcher matcher;
+    buildSimpleAtomFieldMatcher(mTagId, atomField, &matcher);
+    return GetAtomDimensionsValueProto(matcher, dimensionsValue);
 }
 
 DimensionsValue* LogEvent::findFieldValueOrNull(const Field& field) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 5a4efd4..eb2c008 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -92,7 +92,6 @@
      * Get a DimensionsValue proto objects from Field.
      */
     bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const;
-    DimensionsValue  GetSimpleAtomDimensionsValueProto(size_t atomField)  const;
 
     /**
      * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 48f62e7..b6f440f 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -93,25 +93,28 @@
     return matched;
 }
 
-bool matchesNonRepeatedField(
-       const UidMap& uidMap,
-       const FieldValueMap& fieldMap,
-       const FieldValueMatcher&matcher,
-       const Field& field) {
+namespace {
+
+bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
+                      const FieldValueMatcher&matcher, Field* rootField, Field* leafField);
+
+bool matchesNonRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
+                             const FieldValueMatcher&matcher, Field* rootField, Field* leafField) {
     if (matcher.value_matcher_case() ==
             FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) {
         return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field();
     } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) {
         bool allMatched = true;
+        Field* newLeafField = leafField->add_child();
         for (int i = 0; allMatched && i <  matcher.matches_tuple().field_value_matcher_size(); ++i) {
             const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i);
-            Field childField = field;
-            appendLeaf(&childField, childMatcher.field());
-            allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, childField);
+            newLeafField->set_field(childMatcher.field());
+            allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, rootField, newLeafField);
         }
+        leafField->clear_child();
         return allMatched;
     } else {
-        auto ret = fieldMap.equal_range(field);
+        auto ret = fieldMap.equal_range(*rootField);
         int found = 0;
         for (auto it = ret.first; it != ret.second; ++it) {
             found++;
@@ -132,7 +135,7 @@
                  break;
             case FieldValueMatcher::ValueMatcherCase::kEqString:
                  {
-                    if (IsAttributionUidField(field)) {
+                    if (IsAttributionUidField(*rootField)) {
                         const int uid = ret.first->second.value_int();
                         std::set<string> packageNames =
                             uidMap.getAppNamesFromUid(uid, true /* normalize*/);
@@ -171,19 +174,25 @@
 }
 
 bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                          const FieldValueMatcher&matcher, const Field& field) {
+                          const FieldValueMatcher&matcher,
+                          Field* rootField, Field* leafField) {
     if (matcher.position() == Position::FIRST) {
-        Field first_field = field;
-        setPositionForLeaf(&first_field, 0);
-        return matchesNonRepeatedField(uidMap, fieldMap, matcher, first_field);
+        leafField->set_position_index(0);
+        bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
+        leafField->clear_position_index();
+        return res;
     } else {
-        auto itLower = fieldMap.lower_bound(field);
+        auto itLower = fieldMap.lower_bound(*rootField);
         if (itLower == fieldMap.end()) {
             return false;
         }
-        Field next_field = field;
-        getNextField(&next_field);
-        auto itUpper = fieldMap.lower_bound(next_field);
+
+        const int leafFieldNum = leafField->field();
+        leafField->set_field(leafFieldNum + 1);
+        auto itUpper = fieldMap.lower_bound(*rootField);
+        // Resets the field number.
+        leafField->set_field(leafFieldNum);
+
         switch (matcher.position()) {
              case Position::LAST:
                  {
@@ -191,30 +200,30 @@
                      if (itUpper == fieldMap.end()) {
                         return false;
                      } else {
-                         Field last_field = field;
-                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
                          if (last_index < 0) {
                             return false;
                          }
-                         setPositionForLeaf(&last_field, last_index);
-                         return matchesNonRepeatedField(uidMap, fieldMap, matcher, last_field);
+                         leafField->set_position_index(last_index);
+                         bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
+                         leafField->clear_position_index();
+                         return res;
                      }
                  }
                  break;
              case Position::ANY:
                  {
-                    std::set<int> indexes;
-                    for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(field, it->first);
-                        if (index >= 0) {
-                            indexes.insert(index);
-                        }
-                    }
                     bool matched = false;
-                    for (const int index : indexes) {
-                         Field any_field = field;
-                         setPositionForLeaf(&any_field, index);
-                         matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, any_field);
+                    for (auto it = itLower; it != itUpper; ++it) {
+                        int index = getPositionByReferenceField(*rootField, it->first);
+                        if (index >= 0) {
+                             leafField->set_position_index(index);
+                             matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
+                             leafField->clear_position_index();
+                             if (matched) {
+                                break;
+                             }
+                        }
                     }
                     return matched;
                  }
@@ -226,14 +235,16 @@
 }
 
 bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                      const FieldValueMatcher&matcher, const Field& field) {
+                      const FieldValueMatcher&matcher, Field* rootField, Field* leafField) {
     if (!matcher.has_position()) {
-        return matchesNonRepeatedField(uidMap, fieldMap, matcher, field);
+        return matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
     } else {
-        return matchesRepeatedField(uidMap, fieldMap, matcher, field);
+        return matchesRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
     }
 }
 
+}  // namespace
+
 bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
                    const LogEvent& event) {
     if (simpleMatcher.field_value_matcher_size() <= 0) {
@@ -247,13 +258,15 @@
         *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() =
             simpleMatcher.field_value_matcher(i);
     }
-    return matchFieldSimple(uidMap, event.getFieldValueMap(), root_field_matcher, root_field);
+    return matchFieldSimple(
+        uidMap, event.getFieldValueMap(), root_field_matcher, &root_field, &root_field);
 }
 
-vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher) {
-    vector<DimensionsValue> values;
-    findDimensionsValues(event.getFieldValueMap(), matcher, &values);
-    return values;
+void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher,
+                      std::vector<DimensionsValue> *dimensionKeys) {
+    if (matcher.has_field()) {
+        findDimensionsValues(event.getFieldValueMap(), matcher, dimensionKeys);
+    }
 }
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index 704cb4c..a45a9fb 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -42,13 +42,11 @@
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& dimensionsMap,
-                      const FieldValueMatcher& matcher, const Field& field);
-
 bool matchesSimple(const UidMap& uidMap,
     const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
 
-std::vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher);
+void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher,
+                      std::vector<DimensionsValue> *dimensionKeys);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index bfab9e6..000874c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -290,7 +290,8 @@
 
     auto it = mCurrentSlicedDuration.find(eventKey);
 
-    std::vector<DimensionsValue> values = getDimensionKeys(event, mInternalDimensions);
+    std::vector<DimensionsValue> values;
+    getDimensionKeys(event, mInternalDimensions, &values);
     if (values.empty()) {
         if (matcherIndex == mStartIndex) {
             it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 24dc5b0..1072c5a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -58,6 +58,7 @@
 const int FIELD_ID_START_BUCKET_NANOS = 1;
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_ATOM = 3;
+const int FIELD_ID_TIMESTAMP = 4;
 
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
@@ -67,7 +68,7 @@
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId) {
-    mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
+    mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
@@ -77,6 +78,7 @@
     }
     mBucketSizeNs = bucketSizeMills * 1000000;
 
+    mSamplingType = metric.sampling_type();
     mFieldFilter = metric.gauge_fields_filter();
 
     // TODO: use UidMap if uid->pkg_name is required
@@ -89,7 +91,7 @@
     }
 
     // Kicks off the puller immediately.
-    if (mPullTagId != -1) {
+    if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
     }
 
@@ -154,12 +156,23 @@
                                (long long)bucket.mBucketStartNs);
             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
                                (long long)bucket.mBucketEndNs);
-            long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM);
-            writeFieldValueTreeToStream(*bucket.mGaugeFields, protoOutput);
-            protoOutput->end(atomToken);
+
+            if (!bucket.mGaugeAtoms.empty()) {
+                long long atomsToken =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+                for (const auto& atom : bucket.mGaugeAtoms) {
+                    writeFieldValueTreeToStream(*atom.mFields, protoOutput);
+                }
+                protoOutput->end(atomsToken);
+
+                for (const auto& atom : bucket.mGaugeAtoms) {
+                    protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_TIMESTAMP,
+                                       (long long)atom.mTimestamps);
+                }
+            }
             protoOutput->end(bucketInfoToken);
-            VLOG("\t bucket [%lld - %lld] includes %d gauge fields.", (long long)bucket.mBucketStartNs,
-                 (long long)bucket.mBucketEndNs, (int)bucket.mGaugeFields->size());
+            VLOG("\t bucket [%lld - %lld] includes %d atoms.", (long long)bucket.mBucketStartNs,
+                 (long long)bucket.mBucketEndNs, (int)bucket.mGaugeAtoms.size());
         }
         protoOutput->end(wrapperToken);
     }
@@ -181,14 +194,26 @@
     if (mPullTagId == -1) {
         return;
     }
-    // No need to pull again. Either scheduled pull or condition on true happened
-    if (!mCondition) {
+
+    bool triggerPuller = false;
+    switch(mSamplingType) {
+        // When the metric wants to do random sampling and there is already one gauge atom for the
+        // current bucket, do not do it again.
+        case GaugeMetric::RANDOM_ONE_SAMPLE: {
+            triggerPuller = mCondition && mCurrentSlicedBucket->empty();
+            break;
+        }
+        case GaugeMetric::ALL_CONDITION_CHANGES: {
+            triggerPuller = true;
+            break;
+        }
+        default:
+            break;
+    }
+    if (!triggerPuller) {
         return;
     }
-    // Already have gauge metric for the current bucket, do not do it again.
-    if (mCurrentSlicedBucket->size() > 0) {
-        return;
-    }
+
     vector<std::shared_ptr<LogEvent>> allData;
     if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
         ALOGE("Stats puller failed for tag: %d", mPullTagId);
@@ -257,20 +282,24 @@
     }
     flushIfNeededLocked(eventTimeNs);
 
-    // For gauge metric, we just simply use the first gauge in the given bucket.
-    if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end()) {
+    // When gauge metric wants to randomly sample the output atom, we just simply use the first
+    // gauge in the given bucket.
+    if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end() &&
+        mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         return;
     }
-    std::shared_ptr<FieldValueMap> gaugeFields = getGaugeFields(event);
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
-    (*mCurrentSlicedBucket)[eventKey] = gaugeFields;
+    GaugeAtom gaugeAtom;
+    gaugeAtom.mFields = getGaugeFields(event);
+    gaugeAtom.mTimestamps = eventTimeNs;
+    (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
     // Anomaly detection on gauge metric only works when there is one numeric
     // field specified.
     if (mAnomalyTrackers.size() > 0) {
-        if (gaugeFields->size() == 1) {
-            const DimensionsValue& dimensionsValue = gaugeFields->begin()->second;
+        if (gaugeAtom.mFields->size() == 1) {
+            const DimensionsValue& dimensionsValue = gaugeAtom.mFields->begin()->second;
             long gaugeVal = 0;
             if (dimensionsValue.has_value_int()) {
                 gaugeVal = (long)dimensionsValue.value_int();
@@ -289,7 +318,10 @@
     mCurrentSlicedBucketForAnomaly->clear();
     status_t err = NO_ERROR;
     for (const auto& slice : *mCurrentSlicedBucket) {
-        const DimensionsValue& dimensionsValue = slice.second->begin()->second;
+        if (slice.second.empty() || slice.second.front().mFields->empty()) {
+            continue;
+        }
+        const DimensionsValue& dimensionsValue = slice.second.front().mFields->begin()->second;
         long gaugeVal = 0;
         if (dimensionsValue.has_value_int()) {
             gaugeVal = (long)dimensionsValue.value_int();
@@ -318,7 +350,7 @@
     info.mBucketNum = mCurrentBucketNum;
 
     for (const auto& slice : *mCurrentSlicedBucket) {
-        info.mGaugeFields = slice.second;
+        info.mGaugeAtoms = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
         VLOG("gauge metric %lld, dump key value: %s",
@@ -334,7 +366,7 @@
     }
 
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
-    mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
+    mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
 
     // Adjusts the bucket start time
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 1895edf..6c01347 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -32,15 +32,20 @@
 namespace os {
 namespace statsd {
 
+struct GaugeAtom {
+    std::shared_ptr<FieldValueMap> mFields;
+    int64_t mTimestamps;
+};
+
 struct GaugeBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
-    std::shared_ptr<FieldValueMap> mGaugeFields;
+    std::vector<GaugeAtom> mGaugeAtoms;
     uint64_t mBucketNum;
 };
 
-typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<FieldValueMap>>
-    DimToGaugeFieldsMap;
+typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+    DimToGaugeAtomsMap;
 
 // This gauge metric producer first register the puller to automatically pull the gauge at the
 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
@@ -48,7 +53,7 @@
 // producer always reports the guage at the earliest time of the bucket when the condition is met.
 class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
+    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
                         const int pullTagId, const int64_t startTimeNs);
 
@@ -97,7 +102,7 @@
     std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current bucket.
-    std::shared_ptr<DimToGaugeFieldsMap> mCurrentSlicedBucket;
+    std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
 
     // The current bucket for anomaly detection.
     std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
@@ -108,6 +113,8 @@
     // Whitelist of fields to report. Empty means all are reported.
     FieldFilter mFieldFilter;
 
+    GaugeMetric::SamplingType mSamplingType;
+
     // apply a whitelist on the original input
     std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index d620a7e..e74924a 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -32,8 +32,7 @@
     ConditionKey conditionKey;
     if (mConditionSliced) {
         for (const auto& link : mConditionLinks) {
-            conditionKey.insert(std::make_pair(link.condition(),
-                                               getDimensionKeysForCondition(event, link)));
+            getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
         }
         if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
             condition = false;
@@ -44,8 +43,9 @@
         condition = mCondition;
     }
 
-    if (mDimensions.child_size() > 0) {
-        vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions);
+    if (mDimensions.has_field() && mDimensions.child_size() > 0) {
+        vector<DimensionsValue> dimensionValues;
+        getDimensionKeys(event, mDimensions, &dimensionValues);
         for (const DimensionsValue& dimensionValue : dimensionValues) {
             onMatchedLogEventInternalLocked(
                 matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index e988ebc..661d131 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -51,7 +51,7 @@
     void dumpStates(FILE* out, bool verbose) const override;
 
 private:
-    std::map<HashableDimensionKey, DurationInfo> mInfos;
+    std::unordered_map<HashableDimensionKey, DurationInfo> mInfos;
 
     void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                               const uint64_t timestamp);
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 1b74ed1..43469ca 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -56,10 +56,10 @@
     // 2) which keys are paused (started but condition was false)
     // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
     //    it means everything has stopped, we then record the end time.
-    std::map<HashableDimensionKey, int> mStarted;
-    std::map<HashableDimensionKey, int> mPaused;
+    std::unordered_map<HashableDimensionKey, int> mStarted;
+    std::unordered_map<HashableDimensionKey, int> mPaused;
     int64_t mLastStartTime;
-    std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+    std::unordered_map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
 
     // return true if we should not allow newKey to be tracked because we are above the threshold
     bool hitGuardRail(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index a4ccbd4..af21ca4 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -100,7 +100,9 @@
 
   optional int64 end_bucket_nanos = 2;
 
-  optional Atom atom = 3;
+  repeated Atom atom = 3;
+
+  repeated int64 timestamp_nanos = 4;
 }
 
 message GaugeMetricData {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 07bbcb2..2ea79a6 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -222,6 +222,12 @@
   optional TimeUnit bucket = 6;
 
   repeated MetricConditionLink links = 7;
+
+  enum SamplingType {
+    RANDOM_ONE_SAMPLE = 1;
+    ALL_CONDITION_CHANGES = 2;
+  }
+  optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ;
 }
 
 message ValueMetric {
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
index e56a6c5..a80fdc5 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -153,23 +153,26 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid1);
     EXPECT_EQ(data.bucket_info_size(), 3);
+    EXPECT_EQ(data.bucket_info(0).atom_size(), 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(0).atom().app_start_changed().type(), AppStartChanged::HOT);
-    EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name2");
-    EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 102L);
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::HOT);
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name2");
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 102L);
 
+    EXPECT_EQ(data.bucket_info(1).atom_size(), 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.bucket_info(1).atom().app_start_changed().type(), AppStartChanged::WARM);
-    EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_name(), "activity_name4");
-    EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_start_msec(), 104L);
+    EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().type(), AppStartChanged::WARM);
+    EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_name(), "activity_name4");
+    EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_start_msec(), 104L);
 
+    EXPECT_EQ(data.bucket_info(2).atom_size(), 1);
     EXPECT_EQ(data.bucket_info(2).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
     EXPECT_EQ(data.bucket_info(2).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().type(), AppStartChanged::COLD);
-    EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_name(), "activity_name5");
-    EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_start_msec(), 105L);
+    EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().type(), AppStartChanged::COLD);
+    EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_name(), "activity_name5");
+    EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_start_msec(), 105L);
 
     data = gaugeMetrics.data(1);
 
@@ -178,11 +181,12 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid2);
     EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).atom_size(), 1);
     EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
     EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().type(), AppStartChanged::COLD);
-    EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name7");
-    EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 201L);
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::COLD);
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name7");
+    EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 201L);
 }
 
 #else
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 897328d..4ad2097 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -149,8 +149,8 @@
     metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
     link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-    *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
-    *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
+    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
+    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     event1.write("111");  // uid
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 34cde60..da00cae 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -98,8 +98,8 @@
     metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
     link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-    *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
-    *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
+    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
+    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     EXPECT_TRUE(event1.write("111"));
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 82772d8..4533ac6 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -78,7 +78,7 @@
 
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+    auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
     EXPECT_EQ(10, it->second.value_int());
     it++;
     EXPECT_EQ(11, it->second.value_int());
@@ -94,14 +94,14 @@
     allData.push_back(event2);
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+    it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
     EXPECT_EQ(24, it->second.value_int());
     it++;
     EXPECT_EQ(25, it->second.value_int());
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
     EXPECT_EQ(10L, it->second.value_int());
     it++;
     EXPECT_EQ(11L, it->second.value_int());
@@ -112,7 +112,7 @@
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
     EXPECT_EQ(24L, it->second.value_int());
     it++;
     EXPECT_EQ(25L, it->second.value_int());
@@ -151,7 +151,8 @@
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(100,
-        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
+        gaugeProducer.mCurrentSlicedBucket->begin()->
+            second.front().mFields->begin()->second.value_int());
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
@@ -165,17 +166,18 @@
 
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(110,
-        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
+        gaugeProducer.mCurrentSlicedBucket->begin()->
+            second.front().mFields->begin()->second.value_int());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back()
-        .mGaugeFields->begin()->second.value_int());
+        .mGaugeAtoms.front().mFields->begin()->second.value_int());
 
     gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
     gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
     EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back()
-        .mGaugeFields->begin()->second.value_int());
+        .mGaugeAtoms.front().mFields->begin()->second.value_int());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 }
 
@@ -214,7 +216,8 @@
     gaugeProducer.onDataPulled({event1});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(13L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
+        gaugeProducer.mCurrentSlicedBucket->begin()->
+            second.front().mFields->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     std::shared_ptr<LogEvent> event2 =
@@ -226,7 +229,8 @@
     gaugeProducer.onDataPulled({event2});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(15L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
+        gaugeProducer.mCurrentSlicedBucket->begin()->
+            second.front().mFields->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
@@ -239,7 +243,8 @@
     gaugeProducer.onDataPulled({event3});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(26L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
+        gaugeProducer.mCurrentSlicedBucket->begin()->
+            second.front().mFields->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
@@ -250,7 +255,7 @@
     event4->init();
     gaugeProducer.onDataPulled({event4});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second->empty());
+    EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
 }
 
 }  // namespace statsd
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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e7d0c8b..fdb0ac9 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6338,7 +6338,7 @@
 
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
 
-        final AutofillManager afm = mAutofillManager;
+        final AutofillManager afm = getAutofillManager();
         if (afm != null) {
             afm.dump(prefix, writer);
         } else {
@@ -7143,7 +7143,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 3dedeea..29e091b 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
@@ -389,7 +390,7 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        public LoadedApk loadedApk;
+        public LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
@@ -432,7 +433,7 @@
             this.isForward = isForward;
             this.profilerInfo = profilerInfo;
             this.overrideConfig = overrideConfig;
-            this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo,
+            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
                     compatInfo);
             init();
         }
@@ -614,7 +615,7 @@
     }
 
     static final class AppBindData {
-        LoadedApk loadedApk;
+        LoadedApk info;
         String processName;
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
@@ -1913,13 +1914,13 @@
         return mH;
     }
 
-    public final LoadedApk getLoadedApkForPackageName(String packageName,
-            CompatibilityInfo compatInfo, int flags) {
-        return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags) {
+        return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
     }
 
-    public final LoadedApk getLoadedApkForPackageName(String packageName,
-            CompatibilityInfo compatInfo, int flags, int userId) {
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags, int userId) {
         final boolean differentUser = (UserHandle.myUserId() != userId);
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
@@ -1932,13 +1933,13 @@
                 ref = mResourcePackages.get(packageName);
             }
 
-            LoadedApk loadedApk = ref != null ? ref.get() : null;
-            //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
-            //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
-            //        + ": " + loadedApk.mResources.getAssets().isUpToDate());
-            if (loadedApk != null && (loadedApk.mResources == null
-                    || loadedApk.mResources.getAssets().isUpToDate())) {
-                if (loadedApk.isSecurityViolation()
+            LoadedApk packageInfo = ref != null ? ref.get() : null;
+            //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
+            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
+            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
+            if (packageInfo != null && (packageInfo.mResources == null
+                    || packageInfo.mResources.getAssets().isUpToDate())) {
+                if (packageInfo.isSecurityViolation()
                         && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                     throw new SecurityException(
                             "Requesting code from " + packageName
@@ -1946,7 +1947,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return loadedApk;
+                return packageInfo;
             }
         }
 
@@ -1961,13 +1962,13 @@
         }
 
         if (ai != null) {
-            return getLoadedApk(ai, compatInfo, flags);
+            return getPackageInfo(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
+    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
             int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
@@ -1989,17 +1990,17 @@
                 throw new SecurityException(msg);
             }
         }
-        return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
+        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
                 registerPackage);
     }
 
     @Override
-    public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
+    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
-        return getLoadedApk(ai, compatInfo, null, false, true, false);
+        return getPackageInfo(ai, compatInfo, null, false, true, false);
     }
 
-    public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
+    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
@@ -2011,7 +2012,7 @@
         }
     }
 
-    private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
             boolean registerPackage) {
         final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -2026,35 +2027,35 @@
                 ref = mResourcePackages.get(aInfo.packageName);
             }
 
-            LoadedApk loadedApk = ref != null ? ref.get() : null;
-            if (loadedApk == null || (loadedApk.mResources != null
-                    && !loadedApk.mResources.getAssets().isUpToDate())) {
+            LoadedApk packageInfo = ref != null ? ref.get() : null;
+            if (packageInfo == null || (packageInfo.mResources != null
+                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                 if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                         : "Loading resource-only package ") + aInfo.packageName
                         + " (in " + (mBoundApplication != null
                                 ? mBoundApplication.processName : null)
                         + ")");
-                loadedApk =
+                packageInfo =
                     new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
 
                 if (mSystemThread && "android".equals(aInfo.packageName)) {
-                    loadedApk.installSystemApplicationInfo(aInfo,
-                            getSystemContext().mLoadedApk.getClassLoader());
+                    packageInfo.installSystemApplicationInfo(aInfo,
+                            getSystemContext().mPackageInfo.getClassLoader());
                 }
 
                 if (differentUser) {
                     // Caching not supported across users
                 } else if (includeCode) {
                     mPackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(loadedApk));
+                            new WeakReference<LoadedApk>(packageInfo));
                 } else {
                     mResourcePackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(loadedApk));
+                            new WeakReference<LoadedApk>(packageInfo));
                 }
             }
-            return loadedApk;
+            return packageInfo;
         }
     }
 
@@ -2778,8 +2779,8 @@
     /**  Core implementation of activity launch. */
     private Activity performLaunchActivity(ActivityClientRecord r) {
         ActivityInfo aInfo = r.activityInfo;
-        if (r.loadedApk == null) {
-            r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
+        if (r.packageInfo == null) {
+            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -2816,15 +2817,15 @@
         }
 
         try {
-            Application app = r.loadedApk.makeApplication(false, mInstrumentation);
+            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
             if (localLOGV) Slog.v(
                     TAG, r + ": app=" + app
                     + ", appName=" + app.getPackageName()
-                    + ", pkg=" + r.loadedApk.getPackageName()
+                    + ", pkg=" + r.packageInfo.getPackageName()
                     + ", comp=" + r.intent.getComponent().toShortString()
-                    + ", dir=" + r.loadedApk.getAppDir());
+                    + ", dir=" + r.packageInfo.getAppDir());
 
             if (activity != null) {
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2969,7 +2970,7 @@
         }
 
         ContextImpl appContext = ContextImpl.createActivityContext(
-                this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
+                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
 
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
@@ -2977,7 +2978,7 @@
         // its content on a secondary display if there is one.
         String pkgName = SystemProperties.get("debug.second-display.pkg");
         if (pkgName != null && !pkgName.isEmpty()
-                && r.loadedApk.mPackageName.contains(pkgName)) {
+                && r.packageInfo.mPackageName.contains(pkgName)) {
             for (int id : dm.getDisplayIds()) {
                 if (id != Display.DEFAULT_DISPLAY) {
                     Display display =
@@ -3309,7 +3310,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(
+        LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3318,7 +3319,7 @@
         BroadcastReceiver receiver;
         ContextImpl context;
         try {
-            app = loadedApk.makeApplication(false, mInstrumentation);
+            app = packageInfo.makeApplication(false, mInstrumentation);
             context = (ContextImpl) app.getBaseContext();
             if (data.info.splitName != null) {
                 context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3327,7 +3328,7 @@
             data.intent.setExtrasClassLoader(cl);
             data.intent.prepareToEnterProcess();
             data.setExtrasClassLoader(cl);
-            receiver = loadedApk.getAppFactory()
+            receiver = packageInfo.getAppFactory()
                     .instantiateReceiver(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (DEBUG_BROADCAST) Slog.i(TAG,
@@ -3343,9 +3344,9 @@
                 TAG, "Performing receive of " + data.intent
                 + ": app=" + app
                 + ", appName=" + app.getPackageName()
-                + ", pkg=" + loadedApk.getPackageName()
+                + ", pkg=" + packageInfo.getPackageName()
                 + ", comp=" + data.intent.getComponent().toShortString()
-                + ", dir=" + loadedApk.getAppDir());
+                + ", dir=" + packageInfo.getAppDir());
 
             sCurrentBroadcastIntent.set(data.intent);
             receiver.setPendingResult(data);
@@ -3390,8 +3391,8 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
-        String packageName = loadedApk.mPackageName;
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        String packageName = packageInfo.mPackageName;
         if (packageName == null) {
             Slog.d(TAG, "Asked to create backup agent for nonexistent package");
             return;
@@ -3417,11 +3418,11 @@
                 try {
                     if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
 
-                    java.lang.ClassLoader cl = loadedApk.getClassLoader();
+                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
                     agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                     // set up the agent's context
-                    ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                     context.setOuterContext(agent);
                     agent.attach(context);
 
@@ -3457,8 +3458,8 @@
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
-        String packageName = loadedApk.mPackageName;
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        String packageName = packageInfo.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
             try {
@@ -3478,12 +3479,12 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(
+        LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
-            java.lang.ClassLoader cl = loadedApk.getClassLoader();
-            service = loadedApk.getAppFactory()
+            java.lang.ClassLoader cl = packageInfo.getClassLoader();
+            service = packageInfo.getAppFactory()
                     .instantiateService(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (!mInstrumentation.onException(service, e)) {
@@ -3496,10 +3497,10 @@
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
-            ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
+            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
             context.setOuterContext(service);
 
-            Application app = loadedApk.makeApplication(false, mInstrumentation);
+            Application app = packageInfo.makeApplication(false, mInstrumentation);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
@@ -3969,8 +3970,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 +3993,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 +4021,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 +4051,7 @@
             }
         }
 
-        return !r.activity.mFinished && saveState ? r.state : null;
+        return shouldSaveState ? r.state : null;
     }
 
     private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
@@ -4149,32 +4152,42 @@
                 }
             }
 
-            // 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 "
-                                + 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);
+                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) {
+        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
+                && !r.isPreHoneycomb();
+        if (shouldSaveState) {
+            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);
+    }
+
     private void updateVisibility(ActivityClientRecord r, boolean show) {
         View v = r.activity.mDecor;
         if (v != null) {
@@ -4292,24 +4305,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.
@@ -4363,11 +4359,11 @@
     }
 
     private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
-        LoadedApk apk = peekLoadedApk(data.pkg, false);
+        LoadedApk apk = peekPackageInfo(data.pkg, false);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
-        apk = peekLoadedApk(data.pkg, true);
+        apk = peekPackageInfo(data.pkg, true);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
@@ -4459,21 +4455,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 +4761,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 +4798,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 +4813,7 @@
         }
     }
 
-    private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
+    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
         r.state = new Bundle();
         r.state.setAllowFds(false);
         if (r.isPersistable()) {
@@ -4861,7 +4842,7 @@
                 if (a != null) {
                     Configuration thisConfig = applyConfigCompatMainThread(
                             mCurDefaultDisplayDpi, newConfig,
-                            ar.loadedApk.getCompatibilityInfo());
+                            ar.packageInfo.getCompatibilityInfo());
                     if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                         // If the activity is currently resumed, its configuration
                         // needs to change right now.
@@ -5347,7 +5328,7 @@
     }
 
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
-        boolean hasLoadedApk = false;
+        boolean hasPkgInfo = false;
         switch (cmd) {
             case ApplicationThreadConstants.PACKAGE_REMOVED:
             case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5358,14 +5339,14 @@
                 }
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
-                        if (!hasLoadedApk) {
+                        if (!hasPkgInfo) {
                             WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
                             if (ref != null && ref.get() != null) {
-                                hasLoadedApk = true;
+                                hasPkgInfo = true;
                             } else {
                                 ref = mResourcePackages.get(packages[i]);
                                 if (ref != null && ref.get() != null) {
-                                    hasLoadedApk = true;
+                                    hasPkgInfo = true;
                                 }
                             }
                         }
@@ -5385,21 +5366,21 @@
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
                         WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
-                        LoadedApk loadedApk = ref != null ? ref.get() : null;
-                        if (loadedApk != null) {
-                            hasLoadedApk = true;
+                        LoadedApk pkgInfo = ref != null ? ref.get() : null;
+                        if (pkgInfo != null) {
+                            hasPkgInfo = true;
                         } else {
                             ref = mResourcePackages.get(packages[i]);
-                            loadedApk = ref != null ? ref.get() : null;
-                            if (loadedApk != null) {
-                                hasLoadedApk = true;
+                            pkgInfo = ref != null ? ref.get() : null;
+                            if (pkgInfo != null) {
+                                hasPkgInfo = true;
                             }
                         }
                         // If the package is being replaced, yet it still has a valid
                         // LoadedApk object, the package was updated with _DONT_KILL.
                         // Adjust it's internal references to the application info and
                         // resources.
-                        if (loadedApk != null) {
+                        if (pkgInfo != null) {
                             try {
                                 final String packageName = packages[i];
                                 final ApplicationInfo aInfo =
@@ -5413,13 +5394,13 @@
                                         if (ar.activityInfo.applicationInfo.packageName
                                                 .equals(packageName)) {
                                             ar.activityInfo.applicationInfo = aInfo;
-                                            ar.loadedApk = loadedApk;
+                                            ar.packageInfo = pkgInfo;
                                         }
                                     }
                                 }
                                 final List<String> oldPaths =
                                         sPackageManager.getPreviousCodePaths(packageName);
-                                loadedApk.updateApplicationInfo(aInfo, oldPaths);
+                                pkgInfo.updateApplicationInfo(aInfo, oldPaths);
                             } catch (RemoteException e) {
                             }
                         }
@@ -5428,7 +5409,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
     }
 
     final void handleLowMemory() {
@@ -5636,10 +5617,10 @@
             applyCompatConfiguration(mCurDefaultDisplayDpi);
         }
 
-        data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         if (agent != null) {
-            handleAttachAgent(agent, data.loadedApk);
+            handleAttachAgent(agent, data.info);
         }
 
         /**
@@ -5684,7 +5665,7 @@
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
             if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
-                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+                Slog.w(TAG, "Application " + data.info.getPackageName()
                       + " is waiting for the debugger on port 8100...");
 
                 IActivityManager mgr = ActivityManager.getService();
@@ -5703,7 +5684,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+                Slog.w(TAG, "Application " + data.info.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5751,14 +5732,14 @@
             mInstrumentationAppDir = ii.sourceDir;
             mInstrumentationSplitAppDirs = ii.splitSourceDirs;
             mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
-            mInstrumentedAppDir = data.loadedApk.getAppDir();
-            mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
-            mInstrumentedLibDir = data.loadedApk.getLibDir();
+            mInstrumentedAppDir = data.info.getAppDir();
+            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+            mInstrumentedLibDir = data.info.getLibDir();
         } else {
             ii = null;
         }
 
-        final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
+        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
         updateLocaleListFromAppContext(appContext,
                 mResourcesManager.getConfiguration().getLocales());
 
@@ -5802,9 +5783,9 @@
             }
             ii.copyTo(instrApp);
             instrApp.initForUser(UserHandle.myUserId());
-            final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
+            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true, false);
-            final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
+            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
@@ -5849,7 +5830,7 @@
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
-            app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
+            app = data.info.makeApplication(data.restrictedBackupMode, null);
             mInitialApplication = app;
 
             // don't bring up providers in restricted mode; they may depend on the
@@ -5903,7 +5884,7 @@
                 final int preloadedFontsResource = info.metaData.getInt(
                         ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
                 if (preloadedFontsResource != 0) {
-                    data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
+                    data.info.getResources().preloadFonts(preloadedFontsResource);
                 }
             }
         } catch (RemoteException e) {
@@ -6361,12 +6342,12 @@
 
             try {
                 final java.lang.ClassLoader cl = c.getClassLoader();
-                LoadedApk loadedApk = peekLoadedApk(ai.packageName, true);
-                if (loadedApk == null) {
+                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
+                if (packageInfo == null) {
                     // System startup case.
-                    loadedApk = getSystemContext().mLoadedApk;
+                    packageInfo = getSystemContext().mPackageInfo;
                 }
-                localProvider = loadedApk.getAppFactory()
+                localProvider = packageInfo.getAppFactory()
                         .instantiateProvider(cl, info.name);
                 provider = localProvider.getIContentProvider();
                 if (provider == null) {
@@ -6515,8 +6496,8 @@
                 mInstrumentation = new Instrumentation();
                 mInstrumentation.basicInit(this);
                 ContextImpl context = ContextImpl.createAppContext(
-                        this, getSystemContext().mLoadedApk);
-                mInitialApplication = context.mLoadedApk.makeApplication(true, null);
+                        this, getSystemContext().mPackageInfo);
+                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                 mInitialApplication.onCreate();
             } catch (Exception e) {
                 throw new RuntimeException(
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 5a739ea..a13ac49 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -197,7 +197,7 @@
      */
     /* package */ final void attach(Context context) {
         attachBaseContext(context);
-        mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
+        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
     }
 
     /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cc68c05..6b0a2f9 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1409,7 +1409,7 @@
                     sameUid ? app.sourceDir : app.publicSourceDir,
                     sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                     app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                    mContext.mLoadedApk);
+                    mContext.mPackageInfo);
         if (r != null) {
             return r;
         }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 0f66652..5b61fdf 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -111,7 +111,7 @@
             PendingTransactionActions pendingActions);
 
     /** Get package info. */
-    public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
+    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo);
 
     /** Deliver app configuration change notification. */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ea94042..6496110 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -159,7 +159,7 @@
     private ArrayMap<String, File> mSharedPrefsPaths;
 
     final @NonNull ActivityThread mMainThread;
-    final @NonNull LoadedApk mLoadedApk;
+    final @NonNull LoadedApk mPackageInfo;
     private @Nullable ClassLoader mClassLoader;
 
     private final @Nullable IBinder mActivityToken;
@@ -257,8 +257,8 @@
 
     @Override
     public Context getApplicationContext() {
-        return (mLoadedApk != null) ?
-                mLoadedApk.getApplication() : mMainThread.getApplication();
+        return (mPackageInfo != null) ?
+                mPackageInfo.getApplication() : mMainThread.getApplication();
     }
 
     @Override
@@ -302,15 +302,15 @@
 
     @Override
     public ClassLoader getClassLoader() {
-        return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
+        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
     }
 
     @Override
     public String getPackageName() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getPackageName();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getPackageName();
         }
-        // No mLoadedApk means this is a Context for the system itself,
+        // No mPackageInfo means this is a Context for the system itself,
         // and this here is its name.
         return "android";
     }
@@ -329,24 +329,24 @@
 
     @Override
     public ApplicationInfo getApplicationInfo() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getApplicationInfo();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getApplicationInfo();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageResourcePath() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getResDir();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getResDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageCodePath() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getAppDir();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getAppDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
@@ -356,7 +356,7 @@
         // At least one application in the world actually passes in a null
         // name.  This happened to work because when we generated the file name
         // we would stringify it to "null.xml".  Nice.
-        if (mLoadedApk.getApplicationInfo().targetSdkVersion <
+        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                 Build.VERSION_CODES.KITKAT) {
             if (name == null) {
                 name = "null";
@@ -1104,11 +1104,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1208,11 +1208,11 @@
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1262,11 +1262,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1344,11 +1344,11 @@
             Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1425,11 +1425,11 @@
             Handler scheduler, Context context, int flags) {
         IIntentReceiver rd = null;
         if (receiver != null) {
-            if (mLoadedApk != null && context != null) {
+            if (mPackageInfo != null && context != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     receiver, context, scheduler,
                     mMainThread.getInstrumentation(), true);
             } else {
@@ -1456,8 +1456,8 @@
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
-        if (mLoadedApk != null) {
-            IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
+        if (mPackageInfo != null) {
+            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
                     getOuterContext(), receiver);
             try {
                 ActivityManager.getService().unregisterReceiver(rd);
@@ -1590,7 +1590,7 @@
     @Override
     public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
             int flags) {
-        return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
     }
 
     /** @hide */
@@ -1612,16 +1612,16 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mLoadedApk != null) {
-            sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        if (mPackageInfo != null) {
+            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
         } else {
             throw new RuntimeException("Not supported in system context");
         }
         validateServiceIntent(service);
         try {
             IBinder token = getActivityToken();
-            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
-                    && mLoadedApk.getApplicationInfo().targetSdkVersion
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
+                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
@@ -1645,8 +1645,8 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mLoadedApk != null) {
-            IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
+        if (mPackageInfo != null) {
+            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
                     getOuterContext(), conn);
             try {
                 ActivityManager.getService().unbindService(sd);
@@ -1991,20 +1991,40 @@
         }
     }
 
+    private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+        final String[] splitResDirs;
+        final ClassLoader classLoader;
+        try {
+            splitResDirs = pi.getSplitPaths(splitName);
+            classLoader = pi.getSplitClassLoader(splitName);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return ResourcesManager.getInstance().getResources(activityToken,
+                pi.getResDir(),
+                splitResDirs,
+                pi.getOverlayDirs(),
+                pi.getApplicationInfo().sharedLibraryFiles,
+                displayId,
+                overrideConfig,
+                compatInfo,
+                classLoader);
+    }
+
     @Override
     public Context createApplicationContext(ApplicationInfo application, int flags)
             throws NameNotFoundException {
-        LoadedApk loadedApk = mMainThread.getLoadedApk(application,
-                mResources.getCompatibilityInfo(),
+        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
-        if (loadedApk != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
+        if (pi != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2028,21 +2048,20 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
+            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
                     flags, null);
         }
 
-        LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
-                mResources.getCompatibilityInfo(),
+        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
-        if (loadedApk != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
+        if (pi != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
                     flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2056,21 +2075,30 @@
 
     @Override
     public Context createContextForSplit(String splitName) throws NameNotFoundException {
-        if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
             // All Splits are always loaded.
             return this;
         }
 
-        final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
+        final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
+        final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
-        final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
+        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
         final int displayId = mDisplay != null
                 ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-        context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
-                mActivityToken, displayId));
+        context.setResources(ResourcesManager.getInstance().getResources(
+                mActivityToken,
+                mPackageInfo.getResDir(),
+                paths,
+                mPackageInfo.getOverlayDirs(),
+                mPackageInfo.getApplicationInfo().sharedLibraryFiles,
+                displayId,
+                null,
+                mPackageInfo.getCompatibilityInfo(),
+                classLoader));
         return context;
     }
 
@@ -2080,11 +2108,11 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
-        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
@@ -2095,11 +2123,11 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
-        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
@@ -2109,7 +2137,7 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2117,7 +2145,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2166,14 +2194,14 @@
 
     @Override
     public File getDataDir() {
-        if (mLoadedApk != null) {
+        if (mPackageInfo != null) {
             File res = null;
             if (isCredentialProtectedStorage()) {
-                res = mLoadedApk.getCredentialProtectedDataDirFile();
+                res = mPackageInfo.getCredentialProtectedDataDirFile();
             } else if (isDeviceProtectedStorage()) {
-                res = mLoadedApk.getDeviceProtectedDataDirFile();
+                res = mPackageInfo.getDeviceProtectedDataDirFile();
             } else {
-                res = mLoadedApk.getDataDirFile();
+                res = mPackageInfo.getDataDirFile();
             }
 
             if (res != null) {
@@ -2224,10 +2252,10 @@
     }
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        LoadedApk loadedApk = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
+        LoadedApk packageInfo = new LoadedApk(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
-        context.setResources(loadedApk.getResources());
+        context.setResources(packageInfo.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
         return context;
@@ -2238,35 +2266,35 @@
      * Make sure that the created system UI context shares the same LoadedApk as the system context.
      */
     static ContextImpl createSystemUiContext(ContextImpl systemContext) {
-        final LoadedApk loadedApk = systemContext.mLoadedApk;
-        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
+        final LoadedApk packageInfo = systemContext.mPackageInfo;
+        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null);
-        context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
-                loadedApk.getCompatibilityInfo()));
+        context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
+                packageInfo.getCompatibilityInfo()));
         return context;
     }
 
-    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
-        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
-        context.setResources(loadedApk.getResources());
+        context.setResources(packageInfo.getResources());
         return context;
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
             Configuration overrideConfiguration) {
-        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
 
-        String[] splitDirs = loadedApk.getSplitResDirs();
-        ClassLoader classLoader = loadedApk.getClassLoader();
+        String[] splitDirs = packageInfo.getSplitResDirs();
+        ClassLoader classLoader = packageInfo.getClassLoader();
 
-        if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
             try {
-                classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
-                splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
+                classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
+                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
             } catch (NameNotFoundException e) {
                 // Nothing above us can handle a NameNotFoundException, better crash.
                 throw new RuntimeException(e);
@@ -2275,14 +2303,14 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                 activityToken, null, 0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
 
         final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
-                ? loadedApk.getCompatibilityInfo()
+                ? packageInfo.getCompatibilityInfo()
                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
 
         final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2290,10 +2318,10 @@
         // Create the base resources for which all configuration contexts for this Activity
         // will be rebased upon.
         context.setResources(resourcesManager.createBaseActivityResources(activityToken,
-                loadedApk.getResDir(),
+                packageInfo.getResDir(),
                 splitDirs,
-                loadedApk.getOverlayDirs(),
-                loadedApk.getApplicationInfo().sharedLibraryFiles,
+                packageInfo.getOverlayDirs(),
+                packageInfo.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfiguration,
                 compatInfo,
@@ -2304,7 +2332,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk loadedApk, @Nullable String splitName,
+            @NonNull LoadedApk packageInfo, @Nullable String splitName,
             @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
             @Nullable ClassLoader classLoader) {
         mOuterContext = this;
@@ -2313,10 +2341,10 @@
         // location for application.
         if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
-            final File dataDir = loadedApk.getDataDirFile();
-            if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
+            final File dataDir = packageInfo.getDataDirFile();
+            if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-            } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
+            } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
             }
         }
@@ -2330,7 +2358,7 @@
         }
         mUser = user;
 
-        mLoadedApk = loadedApk;
+        mPackageInfo = packageInfo;
         mSplitName = splitName;
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
@@ -2341,8 +2369,8 @@
             setResources(container.mResources);
             mDisplay = container.mDisplay;
         } else {
-            mBasePackageName = loadedApk.mPackageName;
-            ApplicationInfo ainfo = loadedApk.getApplicationInfo();
+            mBasePackageName = packageInfo.mPackageName;
+            ApplicationInfo ainfo = packageInfo.getApplicationInfo();
             if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
                 // Special case: system components allow themselves to be loaded in to other
                 // processes.  For purposes of app ops, we must then consider the context as
@@ -2365,7 +2393,7 @@
     }
 
     void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
-        mLoadedApk.installSystemApplicationInfo(info, classLoader);
+        mPackageInfo.installSystemApplicationInfo(info, classLoader);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
@@ -2374,7 +2402,7 @@
 
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
-        mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
+        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
     }
 
     final Context getReceiverRestrictedContext() {
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
index d1c2441..427a0386 100644
--- a/core/java/android/app/EphemeralResolverService.java
+++ b/core/java/android/app/EphemeralResolverService.java
@@ -17,10 +17,20 @@
 package android.app;
 
 import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.InstantAppResolverService.InstantAppResolutionCallback;
+import android.content.Context;
+import android.content.Intent;
 import android.content.pm.EphemeralResolveInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -75,6 +85,7 @@
         return super.getLooper();
     }
 
+    @Override
     void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
             InstantAppResolutionCallback callback) {
         if (DEBUG_EPHEMERAL) {
@@ -90,6 +101,7 @@
         callback.onInstantAppResolveInfo(resultList);
     }
 
+    @Override
     void _onGetInstantAppIntentFilter(int[] digestPrefix, String token,
             String hostName, InstantAppResolutionCallback callback) {
         if (DEBUG_EPHEMERAL) {
diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl
index ae20057..805d8c0 100644
--- a/core/java/android/app/IInstantAppResolver.aidl
+++ b/core/java/android/app/IInstantAppResolver.aidl
@@ -16,15 +16,13 @@
 
 package android.app;
 
-import android.content.Intent;
 import android.os.IRemoteCallback;
 
 /** @hide */
 oneway interface IInstantAppResolver {
-    void getInstantAppResolveInfoList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
+    void getInstantAppResolveInfoList(in int[] digestPrefix,
             String token, int sequence, IRemoteCallback callback);
 
-    void getInstantAppIntentFilterList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
-            String token, IRemoteCallback callback);
-
+    void getInstantAppIntentFilterList(in int[] digestPrefix,
+            String token, String hostName, IRemoteCallback callback);
 }
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 89aff36..c5dc86c 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.SystemApi;
+import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.InstantAppResolveInfo;
@@ -34,7 +35,6 @@
 import com.android.internal.os.SomeArgs;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -53,65 +53,23 @@
     Handler mHandler;
 
     /**
-     * Called to retrieve resolve info for instant applications immediately.
+     * Called to retrieve resolve info for instant applications.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
-     * @deprecated should implement {@link #onGetInstantAppResolveInfo(Intent, int[], String,
-     *             InstantAppResolutionCallback)}
      */
-    @Deprecated
     public void onGetInstantAppResolveInfo(
             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
         throw new IllegalStateException("Must define");
     }
 
     /**
-     * Called to retrieve intent filters for instant applications from potentially expensive
-     * sources.
+     * Called to retrieve intent filters for instant applications.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
-     * @deprecated should implement {@link #onGetInstantAppIntentFilter(Intent, int[], String,
-     *             InstantAppResolutionCallback)}
      */
-    @Deprecated
     public void onGetInstantAppIntentFilter(
             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
-        throw new IllegalStateException("Must define onGetInstantAppIntentFilter");
-    }
-
-    /**
-     * Called to retrieve resolve info for instant applications immediately.
-     *
-     * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
-     * @param hostDigestPrefix The hash prefix of the instant app's domain.
-     */
-    public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix,
-            String token, InstantAppResolutionCallback callback) {
-        // if not overridden, forward to old methods and filter out non-web intents
-        if (sanitizedIntent.isBrowsableWebIntent()) {
-            onGetInstantAppResolveInfo(hostDigestPrefix, token, callback);
-        } else {
-            callback.onInstantAppResolveInfo(Collections.emptyList());
-        }
-    }
-
-    /**
-     * Called to retrieve intent filters for instant applications from potentially expensive
-     * sources.
-     *
-     * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
-     * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
-     *                         defined.
-     */
-    public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix,
-            String token, InstantAppResolutionCallback callback) {
-        Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden");
-        // if not overridden, forward to old methods and filter out non-web intents
-        if (sanitizedIntent.isBrowsableWebIntent()) {
-            onGetInstantAppIntentFilter(hostDigestPrefix, token, callback);
-        } else {
-            callback.onInstantAppResolveInfo(Collections.emptyList());
-        }
+        throw new IllegalStateException("Must define");
     }
 
     /**
@@ -131,8 +89,8 @@
     public final IBinder onBind(Intent intent) {
         return new IInstantAppResolver.Stub() {
             @Override
-            public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix,
-                    String token, int sequence, IRemoteCallback callback) {
+            public void getInstantAppResolveInfoList(
+                    int digestPrefix[], String token, int sequence, IRemoteCallback callback) {
                 if (DEBUG_EPHEMERAL) {
                     Slog.v(TAG, "[" + token + "] Phase1 called; posting");
                 }
@@ -140,14 +98,14 @@
                 args.arg1 = callback;
                 args.arg2 = digestPrefix;
                 args.arg3 = token;
-                args.arg4 = sanitizedIntent;
-                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO,
-                        sequence, 0, args).sendToTarget();
+                mHandler.obtainMessage(
+                                ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args)
+                        .sendToTarget();
             }
 
             @Override
-            public void getInstantAppIntentFilterList(Intent sanitizedIntent,
-                    int[] digestPrefix, String token, IRemoteCallback callback) {
+            public void getInstantAppIntentFilterList(
+                    int digestPrefix[], String token, String hostName, IRemoteCallback callback) {
                 if (DEBUG_EPHEMERAL) {
                     Slog.v(TAG, "[" + token + "] Phase2 called; posting");
                 }
@@ -155,9 +113,9 @@
                 args.arg1 = callback;
                 args.arg2 = digestPrefix;
                 args.arg3 = token;
-                args.arg4 = sanitizedIntent;
-                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER,
-                        callback).sendToTarget();
+                args.arg4 = hostName;
+                mHandler.obtainMessage(
+                        ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget();
             }
         };
     }
@@ -184,9 +142,29 @@
         }
     }
 
+    @Deprecated
+    void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
+            InstantAppResolutionCallback callback) {
+        if (DEBUG_EPHEMERAL) {
+            Slog.d(TAG, "[" + token + "] Phase1 request;"
+                    + " prefix: " + Arrays.toString(digestPrefix));
+        }
+        onGetInstantAppResolveInfo(digestPrefix, token, callback);
+    }
+    @Deprecated
+    void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
+            InstantAppResolutionCallback callback) {
+        if (DEBUG_EPHEMERAL) {
+            Slog.d(TAG, "[" + token + "] Phase2 request;"
+                    + " prefix: " + Arrays.toString(digestPrefix));
+        }
+        onGetInstantAppIntentFilter(digestPrefix, token, callback);
+    }
+
     private final class ServiceHandler extends Handler {
         public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1;
         public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2;
+
         public ServiceHandler(Looper looper) {
             super(looper, null /*callback*/, true /*async*/);
         }
@@ -201,13 +179,9 @@
                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
                     final int[] digestPrefix = (int[]) args.arg2;
                     final String token = (String) args.arg3;
-                    final Intent intent = (Intent) args.arg4;
                     final int sequence = message.arg1;
-                    if (DEBUG_EPHEMERAL) {
-                        Slog.d(TAG, "[" + token + "] Phase1 request;"
-                                + " prefix: " + Arrays.toString(digestPrefix));
-                    }
-                    onGetInstantAppResolveInfo(intent, digestPrefix, token,
+                    _onGetInstantAppResolveInfo(
+                            digestPrefix, token,
                             new InstantAppResolutionCallback(sequence, callback));
                 } break;
 
@@ -216,12 +190,9 @@
                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
                     final int[] digestPrefix = (int[]) args.arg2;
                     final String token = (String) args.arg3;
-                    final Intent intent = (Intent) args.arg4;
-                    if (DEBUG_EPHEMERAL) {
-                        Slog.d(TAG, "[" + token + "] Phase2 request;"
-                                + " prefix: " + Arrays.toString(digestPrefix));
-                    }
-                    onGetInstantAppIntentFilter(intent, digestPrefix, token,
+                    final String hostName = (String) args.arg4;
+                    _onGetInstantAppIntentFilter(
+                            digestPrefix, token, hostName,
                             new InstantAppResolutionCallback(-1 /*sequence*/, callback));
                 } break;
 
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 198bce6..67f5a2e 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1216,10 +1216,10 @@
                     + " disabling AppComponentFactory", new Throwable());
             return AppComponentFactory.DEFAULT;
         }
-        LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true);
+        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
         // This is in the case of starting up "android".
-        if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk;
-        return loadedApk.getAppFactory();
+        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
+        return apk.getAppFactory();
     }
 
     private void prePerformCreate(Activity activity) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d24d4f3..ea5932c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -32,7 +32,6 @@
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
@@ -964,78 +963,14 @@
                 throw new AssertionError("null split not found");
             }
 
-            mResources = ResourcesManager.getInstance().getResources(
-                    null,
-                    mResDir,
-                    splitPaths,
-                    mOverlayDirs,
-                    mApplicationInfo.sharedLibraryFiles,
-                    Display.DEFAULT_DISPLAY,
-                    null,
-                    getCompatibilityInfo(),
+            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
+                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
+                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                     getClassLoader());
         }
         return mResources;
     }
 
-    public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
-            @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
-        return ResourcesManager.getInstance().getResources(
-                activityToken,
-                mResDir,
-                getSplitPaths(splitName),
-                mOverlayDirs,
-                mApplicationInfo.sharedLibraryFiles,
-                displayId,
-                null,
-                getCompatibilityInfo(),
-                getSplitClassLoader(splitName));
-    }
-
-    /**
-     * Creates the top level resources for the given package. Will return an existing
-     * Resources if one has already been created.
-     */
-    public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
-        // Request for this app, short circuit
-        if (appInfo.uid == Process.myUid()) {
-            return getResources();
-        }
-
-        // Get resources for a different package
-        return ResourcesManager.getInstance().getResources(
-                null,
-                appInfo.publicSourceDir,
-                appInfo.splitPublicSourceDirs,
-                appInfo.resourceDirs,
-                appInfo.sharedLibraryFiles,
-                Display.DEFAULT_DISPLAY,
-                null,
-                getCompatibilityInfo(),
-                getClassLoader());
-    }
-
-    public Resources createResources(IBinder activityToken, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
-        final String[] splitResDirs;
-        final ClassLoader classLoader;
-        try {
-            splitResDirs = getSplitPaths(splitName);
-            classLoader = getSplitClassLoader(splitName);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-        return ResourcesManager.getInstance().getResources(activityToken,
-                mResDir,
-                splitResDirs,
-                mOverlayDirs,
-                mApplicationInfo.sharedLibraryFiles,
-                displayId,
-                overrideConfig,
-                compatInfo,
-                classLoader);
-    }
-
     public Application makeApplication(boolean forceDefaultAppClass,
             Instrumentation instrumentation) {
         if (mApplication != null) {
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/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/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 8304c1c..073d28c 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -134,7 +134,7 @@
                 Bundle.dumpStats(pw, mPersistentState);
 
                 if (ex instanceof TransactionTooLargeException
-                        && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                     Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                     return;
                 }
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..e02a294 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -50,7 +50,6 @@
 import android.provider.DocumentsProvider;
 import android.provider.MediaStore;
 import android.provider.OpenableColumns;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -4473,15 +4472,7 @@
     public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
 
     /**
-     * An array of {@link Bundle}s containing details about resolved instant apps..
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_INSTANT_APP_BUNDLES =
-            "android.intent.extra.INSTANT_APP_BUNDLES";
-
-    /**
-     * A {@link Bundle} of metadata that describes the instant application that needs to be
+     * A {@link Bundle} of metadata that describes the instanta application that needs to be
      * installed. This data is populated from the response to
      * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
      * instant application resolver.
@@ -4491,16 +4482,6 @@
             "android.intent.extra.INSTANT_APP_EXTRAS";
 
     /**
-     * A boolean value indicating that the instant app resolver was unable to state with certainty
-     * that it did or did not have an app for the sanitized {@link Intent} defined at
-     * {@link #EXTRA_INTENT}.
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_UNKNOWN_INSTANT_APP =
-            "android.intent.extra.UNKNOWN_INSTANT_APP";
-
-    /**
      * The version code of the app to install components from.
      * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
      * @hide
@@ -5048,7 +5029,6 @@
             FLAG_GRANT_PREFIX_URI_PERMISSION,
             FLAG_DEBUG_TRIAGED_MISSING,
             FLAG_IGNORE_EPHEMERAL,
-            FLAG_ACTIVITY_MATCH_EXTERNAL,
             FLAG_ACTIVITY_NO_HISTORY,
             FLAG_ACTIVITY_SINGLE_TOP,
             FLAG_ACTIVITY_NEW_TASK,
@@ -5092,7 +5072,6 @@
             FLAG_INCLUDE_STOPPED_PACKAGES,
             FLAG_DEBUG_TRIAGED_MISSING,
             FLAG_IGNORE_EPHEMERAL,
-            FLAG_ACTIVITY_MATCH_EXTERNAL,
             FLAG_ACTIVITY_NO_HISTORY,
             FLAG_ACTIVITY_SINGLE_TOP,
             FLAG_ACTIVITY_NEW_TASK,
@@ -5496,14 +5475,6 @@
      */
     public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
 
-
-    /**
-     * If set, resolution of this intent may take place via an instant app not
-     * yet on the device if there does not yet exist an app on device to
-     * resolve it.
-     */
-    public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
-
     /**
      * If set, when sending a broadcast only registered receivers will be
      * called -- no BroadcastReceiver components will be launched.
@@ -10057,25 +10028,6 @@
         }
     }
 
-    /** @hide */
-    public boolean hasWebURI() {
-        if (getData() == null) {
-            return false;
-        }
-        final String scheme = getScheme();
-        if (TextUtils.isEmpty(scheme)) {
-            return false;
-        }
-        return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
-    }
-
-    /** @hide */
-    public boolean isBrowsableWebIntent() {
-        return ACTION_VIEW.equals(mAction)
-                && hasCategory(CATEGORY_BROWSABLE)
-                && hasWebURI();
-    }
-
     /**
      * @hide
      */
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 0000000..2aaac02
--- /dev/null
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,54 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * included by default.
+ *
+ * <p>This is separated out so that it can be conditionally included at build time depending on
+ * whether android.test.base is on the bootclasspath or not. In order to include this at
+ * build time, and remove android.test.base from the bootclasspath pass
+ * REMOVE_ATB_FROM_BCP=true on the build command line, otherwise this class will not be included
+ * and the
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+
+    @Override
+    public void updatePackage(Package pkg) {
+        // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+        // to be accessible so this maintains backward compatibility by adding the
+        // android.test.base library to those packages.
+        if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+            prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
+        } else {
+            // If a package already depends on android.test.runner then add a dependency on
+            // android.test.base because android.test.runner depends on classes from the
+            // android.test.base library.
+            prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 202df50..6bdcefb 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -21,10 +21,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Bundle;
-
-import java.util.Collections;
-import java.util.List;
 
 /**
  * Auxiliary application resolution response.
@@ -35,95 +31,56 @@
  * hasn't been installed.
  * @hide
  */
-public final class AuxiliaryResolveInfo {
+public final class AuxiliaryResolveInfo extends IntentFilter {
+    /** Resolved information returned from the external instant resolver */
+    public final InstantAppResolveInfo resolveInfo;
+    /** The resolved package. Copied from {@link #resolveInfo}. */
+    public final String packageName;
     /** The activity to launch if there's an installation failure. */
     public final ComponentName installFailureActivity;
+    /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+    public final String splitName;
     /** Whether or not instant resolution needs the second phase */
     public final boolean needsPhaseTwo;
     /** Opaque token to track the instant application resolution */
     public final String token;
+    /** The version code of the package */
+    public final long versionCode;
     /** An intent to start upon failure to install */
     public final Intent failureIntent;
-    /** The matching filters for this resolve info. */
-    public final List<AuxiliaryFilter> filters;
 
     /** Create a response for installing an instant application. */
-    public AuxiliaryResolveInfo(@NonNull String token,
+    public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo,
+            @NonNull IntentFilter orig,
+            @Nullable String splitName,
+            @NonNull String token,
             boolean needsPhase2,
-            @Nullable Intent failureIntent,
-            @Nullable List<AuxiliaryFilter> filters) {
+            @Nullable Intent failureIntent) {
+        super(orig);
+        this.resolveInfo = resolveInfo;
+        this.packageName = resolveInfo.getPackageName();
+        this.splitName = splitName;
         this.token = token;
         this.needsPhaseTwo = needsPhase2;
+        this.versionCode = resolveInfo.getVersionCode();
         this.failureIntent = failureIntent;
-        this.filters = filters;
         this.installFailureActivity = null;
     }
 
     /** Create a response for installing a split on demand. */
-    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
-            @Nullable Intent failureIntent,
-            @Nullable List<AuxiliaryFilter> filters) {
+    public AuxiliaryResolveInfo(@NonNull String packageName,
+            @Nullable String splitName,
+            @Nullable ComponentName failureActivity,
+            long versionCode,
+            @Nullable Intent failureIntent) {
         super();
+        this.packageName = packageName;
         this.installFailureActivity = failureActivity;
-        this.filters = filters;
+        this.splitName = splitName;
+        this.versionCode = versionCode;
+        this.resolveInfo = null;
         this.token = null;
         this.needsPhaseTwo = false;
         this.failureIntent = failureIntent;
     }
-
-    /** Create a response for installing a split on demand. */
-    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
-            String packageName, long versionCode, String splitName) {
-        this(failureActivity, null, Collections.singletonList(
-                new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
-    }
-
-    /** @hide */
-    public static final class AuxiliaryFilter extends IntentFilter {
-        /** Resolved information returned from the external instant resolver */
-        public final InstantAppResolveInfo resolveInfo;
-        /** The resolved package. Copied from {@link #resolveInfo}. */
-        public final String packageName;
-        /** The version code of the package */
-        public final long versionCode;
-        /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
-        public final String splitName;
-        /** The extras to pass on to the installer for this filter. */
-        public final Bundle extras;
-
-        public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
-                String splitName, Bundle extras) {
-            super(orig);
-            this.resolveInfo = resolveInfo;
-            this.packageName = resolveInfo.getPackageName();
-            this.versionCode = resolveInfo.getLongVersionCode();
-            this.splitName = splitName;
-            this.extras = extras;
-        }
-
-        public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
-                String splitName, Bundle extras) {
-            this.resolveInfo = resolveInfo;
-            this.packageName = resolveInfo.getPackageName();
-            this.versionCode = resolveInfo.getLongVersionCode();
-            this.splitName = splitName;
-            this.extras = extras;
-        }
-
-        public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
-            this.resolveInfo = null;
-            this.packageName = packageName;
-            this.versionCode = versionCode;
-            this.splitName = splitName;
-            this.extras = null;
-        }
-
-        @Override
-        public String toString() {
-            return "AuxiliaryFilter{"
-                    + "packageName='" + packageName + '\''
-                    + ", versionCode=" + versionCode
-                    + ", splitName='" + splitName + '\'' + '}';
-        }
-    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 112c5da..19cb932 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.content.Intent;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,35 +26,11 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
 /**
- * Describes an externally resolvable instant application. There are three states that this class
- * can represent: <p/>
- * <ul>
- *     <li>
- *         The first, usable only for non http/s intents, implies that the resolver cannot
- *         immediately resolve this intent and would prefer that resolution be deferred to the
- *         instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
- *         If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
- *         prefixes were passed into one of the resolve methods in
- *         {@link android.app.InstantAppResolverService}, this state cannot be used.
- *     </li>
- *     <li>
- *         The second represents a partial match and is constructed with any of the other
- *         constructors. By setting one or more of the {@link Nullable}arguments to null, you
- *         communicate to the resolver in response to
- *         {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
- *                String, InstantAppResolverService.InstantAppResolutionCallback)}
- *         that you need a 2nd round of resolution to complete the request.
- *     </li>
- *     <li>
- *         The third represents a complete match and is constructed with all @Nullable parameters
- *         populated.
- *     </li>
- * </ul>
+ * Information about an instant application.
  * @hide
  */
 @SystemApi
@@ -63,8 +38,6 @@
     /** Algorithm that will be used to generate the domain digest */
     private static final String SHA_ALGORITHM = "SHA-256";
 
-    private static final byte[] EMPTY_DIGEST = new byte[0];
-
     private final InstantAppDigest mDigest;
     private final String mPackageName;
     /** The filters used to match domain */
@@ -73,30 +46,15 @@
     private final long mVersionCode;
     /** Data about the app that should be passed along to the Instant App installer on resolve */
     private final Bundle mExtras;
-    /**
-     * A flag that indicates that the resolver is aware that an app may match, but would prefer
-     * that the installer get the sanitized intent to decide. This should not be used for
-     * resolutions that include a host and will be ignored in such cases.
-     */
-    private final boolean mShouldLetInstallerDecide;
 
-    /** Constructor for intent-based InstantApp resolution results. */
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
         this(digest, packageName, filters, (long) versionCode, null /* extras */);
     }
 
-    /** Constructor for intent-based InstantApp resolution results with extras. */
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, long versionCode,
             @Nullable Bundle extras) {
-        this(digest, packageName, filters, versionCode, extras, false);
-    }
-
-    /** Constructor for intent-based InstantApp resolution results with extras. */
-    private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
-            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
-            @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -104,7 +62,7 @@
         }
         mDigest = digest;
         if (filters != null) {
-            mFilters = new ArrayList<>(filters.size());
+            mFilters = new ArrayList<InstantAppIntentFilter>(filters.size());
             mFilters.addAll(filters);
         } else {
             mFilters = null;
@@ -112,48 +70,25 @@
         mPackageName = packageName;
         mVersionCode = versionCode;
         mExtras = extras;
-        mShouldLetInstallerDecide = shouldLetInstallerDecide;
     }
 
-    /** Constructor for intent-based InstantApp resolution results by hostname. */
     public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters) {
         this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
                 null /* extras */);
     }
 
-    /**
-     * Constructor that creates a "let the installer decide" response with optional included
-     * extras.
-     */
-    public InstantAppResolveInfo(@Nullable Bundle extras) {
-        this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
-    }
-
     InstantAppResolveInfo(Parcel in) {
-        mShouldLetInstallerDecide = in.readBoolean();
+        mDigest = in.readParcelable(null /*loader*/);
+        mPackageName = in.readString();
+        mFilters = new ArrayList<InstantAppIntentFilter>();
+        in.readList(mFilters, null /*loader*/);
+        mVersionCode = in.readLong();
         mExtras = in.readBundle();
-        if (mShouldLetInstallerDecide) {
-            mDigest = InstantAppDigest.UNDEFINED;
-            mPackageName = null;
-            mFilters = Collections.emptyList();
-            mVersionCode = -1;
-        } else {
-            mDigest = in.readParcelable(null /*loader*/);
-            mPackageName = in.readString();
-            mFilters = new ArrayList<>();
-            in.readList(mFilters, null /*loader*/);
-            mVersionCode = in.readLong();
-        }
-    }
-
-    /** Returns true if the installer should be notified that it should query for packages. */
-    public boolean shouldLetInstallerDecide() {
-        return mShouldLetInstallerDecide;
     }
 
     public byte[] getDigestBytes() {
-        return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
+        return mDigest.getDigestBytes()[0];
     }
 
     public int getDigestPrefix() {
@@ -192,15 +127,11 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeBoolean(mShouldLetInstallerDecide);
-        out.writeBundle(mExtras);
-        if (mShouldLetInstallerDecide) {
-            return;
-        }
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
         out.writeLong(mVersionCode);
+        out.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
@@ -228,9 +159,7 @@
     @SystemApi
     public static final class InstantAppDigest implements Parcelable {
         private static final int DIGEST_MASK = 0xfffff000;
-
-        public static final InstantAppDigest UNDEFINED =
-                new InstantAppDigest(new byte[][]{}, new int[]{});
+        private static final int DIGEST_PREFIX_COUNT = 5;
         /** Full digest of the domain hashes */
         private final byte[][] mDigestBytes;
         /** The first 4 bytes of the domain hashes */
@@ -257,11 +186,6 @@
             }
         }
 
-        private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
-            this.mDigestPrefix = prefix;
-            this.mDigestBytes = digestBytes;
-        }
-
         private static byte[][] generateDigest(String hostName, int maxDigests) {
             ArrayList<byte[]> digests = new ArrayList<>();
             try {
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index 67e4cd5..a16f81b 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
@@ -52,12 +53,22 @@
                 "android.content.pm.OrgApacheHttpLegacyUpdater",
                 RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
 
+        // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
+        // android.test.mock.
         packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
 
+        // Attempt to load and add the optional updater that will only be available when
+        // REMOVE_ATB_FROM_BCP=true. If that could not be found then add the default updater that
+        // will remove any references to org.apache.http.library from the package so that it does
+        // not try and load the library when it is on the bootclasspath.
+        boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
+                "android.content.pm.AndroidTestBaseUpdater",
+                RemoveUnnecessaryAndroidTestBaseLibrary::new);
+
         PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
                 .toArray(new PackageSharedLibraryUpdater[0]);
         INSTANCE = new PackageBackwardCompatibility(
-                bootClassPathContainsOAHL, updaterArray);
+                bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
     }
 
     /**
@@ -105,11 +116,14 @@
 
     private final boolean mBootClassPathContainsOAHL;
 
+    private final boolean mBootClassPathContainsATB;
+
     private final PackageSharedLibraryUpdater[] mPackageUpdaters;
 
     public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
-            PackageSharedLibraryUpdater[] packageUpdaters) {
+            boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
         this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
+        this.mBootClassPathContainsATB = bootClassPathContainsATB;
         this.mPackageUpdaters = packageUpdaters;
     }
 
@@ -140,6 +154,14 @@
     }
 
     /**
+     * True if the android.test.base is on the bootclasspath, false otherwise.
+     */
+    @VisibleForTesting
+    public static boolean bootClassPathContainsATB() {
+        return INSTANCE.mBootClassPathContainsATB;
+    }
+
+    /**
      * Add android.test.mock dependency for any APK that depends on android.test.runner.
      *
      * <p>This is needed to maintain backwards compatibility as in previous versions of Android the
@@ -173,4 +195,18 @@
         }
 
     }
+
+    /**
+     * Remove any usages of android.test.base from the shared library as the library is on the
+     * bootclasspath.
+     */
+    @VisibleForTesting
+    public static class RemoveUnnecessaryAndroidTestBaseLibrary
+            extends PackageSharedLibraryUpdater {
+
+        @Override
+        public void updatePackage(Package pkg) {
+            removeLibrary(pkg, ANDROID_TEST_BASE);
+        }
+    }
 }
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/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 3fbf664..83e8663 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,6 +22,8 @@
  */
 public class SharedLibraryNames {
 
+    static final String ANDROID_TEST_BASE = "android.test.base";
+
     static final String ANDROID_TEST_MOCK = "android.test.mock";
 
     static final String ANDROID_TEST_RUNNER = "android.test.runner";
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/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/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..1f0d683 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -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/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index c4b7715..738eb68 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -71,7 +71,7 @@
         in byte[] entropy);
     int abort(IBinder handle);
     boolean isOperationAuthorized(IBinder token);
-    int addAuthToken(in byte[] authToken, in int androidId);
+    int addAuthToken(in byte[] authToken);
     int onUserAdded(int userId, int parentId);
     int onUserRemoved(int userId);
     int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 479231d..1d13335 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -74,7 +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_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
+    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;
@@ -216,7 +216,6 @@
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
     public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
-    public static final int KM_ERROR_DEVICE_LOCKED = -72;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -263,7 +262,6 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
         sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
-        sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
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/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
similarity index 63%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
copy to core/java/android/service/textclassifier/ITextClassificationCallback.aidl
index 52efd23..10bfe63 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
+++ b/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
@@ -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,15 +14,15 @@
  * limitations under the License.
  */
 
+package android.service.textclassifier;
 
-package android.telephony.ims.internal.aidl;
+import android.view.textclassifier.TextClassification;
 
 /**
- * Provides callback interface for ImsConfig when a value has changed.
- *
- * {@hide}
+ * Callback for a TextClassification request.
+ * @hide
  */
-oneway interface IImsConfigCallback {
-    void onIntConfigChanged(int item, int value);
-    void onStringConfigChanged(int item, String value);
+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/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/core/java/android/service/textclassifier/ITextLinksCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
copy to core/java/android/service/textclassifier/ITextLinksCallback.aidl
index f6005b6..a9e0dde 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
+++ b/core/java/android/service/textclassifier/ITextLinksCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 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,12 +14,15 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.service.textclassifier;
+
+import android.view.textclassifier.TextLinks;
 
 /**
- * See RcsFeature for more information.
- * {@hide}
+ * Callback for a TextLinks request.
+ * @hide
  */
-interface IImsRcsFeature {
-    //Empty Default Implementation
+oneway interface ITextLinksCallback {
+    void onSuccess(in TextLinks links);
+    void onFailure();
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/core/java/android/service/textclassifier/ITextSelectionCallback.aidl
similarity index 64%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
copy to core/java/android/service/textclassifier/ITextSelectionCallback.aidl
index f6005b6..1b4c4d1 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
+++ b/core/java/android/service/textclassifier/ITextSelectionCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 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,12 +14,15 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.service.textclassifier;
+
+import android.view.textclassifier.TextSelection;
 
 /**
- * See RcsFeature for more information.
- * {@hide}
+ * Callback for a TextSelection request.
+ * @hide
  */
-interface IImsRcsFeature {
-    //Empty Default Implementation
+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/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/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/View.java b/core/java/android/view/View.java
index 5150c1f..9770fba 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;
@@ -3975,6 +3974,7 @@
     /**
      * Current clip bounds. to which all drawing of this view are constrained.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     Rect mClipBounds = null;
 
     private boolean mLastIsOpaque;
@@ -26979,6 +26979,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());
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0af1254..7bd197e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1995,6 +1995,7 @@
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
+                surfaceChanged |= surfaceSizeChanged;
                 final boolean alwaysConsumeNavBarChanged =
                         mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
                 if (contentInsetsChanged) {
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/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/core/java/android/view/textclassifier/TextClassification.aidl
similarity index 77%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to core/java/android/view/textclassifier/TextClassification.aidl
index d648a35..9fefe5d 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/core/java/android/view/textclassifier/TextClassification.aidl
@@ -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,6 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.view.textclassifier;
 
-parcelable ImsStreamMediaProfile;
+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..b03c70d 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 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,13 @@
                 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);
             }
         } 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 +254,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 +276,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 +296,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 +336,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 +350,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 +368,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 +376,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 +394,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/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/core/java/android/view/textclassifier/TextLinks.aidl
similarity index 79%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to core/java/android/view/textclassifier/TextLinks.aidl
index d648a35..1bbb798 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/core/java/android/view/textclassifier/TextLinks.aidl
@@ -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,6 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.view.textclassifier;
 
-parcelable ImsStreamMediaProfile;
+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..670efdd 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.style.ClickableSpan;
-import android.view.View;
 import android.widget.TextView;
+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 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_STRAGETY_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_STATEGY_IGNORE
+         * @see #APPLY_STRAGETY_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/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/core/java/android/view/textclassifier/TextSelection.aidl
similarity index 78%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to core/java/android/view/textclassifier/TextSelection.aidl
index d648a35..dab1aef 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/core/java/android/view/textclassifier/TextSelection.aidl
@@ -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,6 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.view.textclassifier;
 
-parcelable ImsStreamMediaProfile;
+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 a4da05f..2e4cccf 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,16 +19,17 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.update.ApiLoader;
+import android.media.update.FrameLayoutHelper;
 import android.media.update.MediaControlView2Provider;
-import android.media.update.ViewProvider;
 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",
@@ -45,10 +46,23 @@
  * developer needs to manually retrieve a MediaController instance and set it to MediaControlView2
  * by calling setController(MediaController controller).
  *
+ * <p>
+ * There is no separate method that handles the show/hide behavior for MediaControlView2. Instead,
+ * one can directly change the visibility of this view by calling View.setVisibility(int). The
+ * values supported are View.VISIBLE and View.GONE.
+ * In addition, the following customizations are supported:
+ * 1. Modify default timeout value of 2 seconds by calling setTimeout(long).
+ * 2. Set focus to the play/pause button by calling requestPlayButtonFocus().
+ *
+ * <p>
+ * It is also possible to add custom buttons with custom icons and actions inside MediaControlView2.
+ * Those buttons will be shown when the overflow button is clicked.
+ * See {@link VideoView2#setCustomActions} for more details on how to add.
+ *
  * TODO PUBLIC API
  * @hide
  */
-public class MediaControlView2 extends FrameLayout {
+public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provider> {
     /** @hide */
     @IntDef({
             BUTTON_PLAY_PAUSE,
@@ -127,8 +141,6 @@
      */
     public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
 
-    private final MediaControlView2Provider mProvider;
-
     public MediaControlView2(@NonNull Context context) {
         this(context, null);
     }
@@ -144,15 +156,16 @@
 
     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());
+        super((instance, superProvider) ->
+                ApiLoader.getProvider(context).createMediaControlView2(
+                        (MediaControlView2) instance, superProvider),
+                context, attrs, defStyleAttr, defStyleRes);
     }
 
     /**
      * @hide
      */
+    @SystemApi
     public MediaControlView2Provider getProvider() {
         return mProvider;
     }
@@ -165,22 +178,6 @@
     }
 
     /**
-     * Shows the control view on screen. It will disappear automatically after 3 seconds of
-     * inactivity.
-     */
-    public void show() {
-        mProvider.show_impl();
-    }
-
-    /**
-     * Shows the control view on screen. It will disappear automatically after {@code timeout}
-     * milliseconds of inactivity.
-     */
-    public void show(long timeout) {
-        mProvider.show_impl(timeout);
-    }
-
-    /**
      * Returns whether the control view is currently shown or hidden.
      */
     public boolean isShowing() {
@@ -188,13 +185,6 @@
     }
 
     /**
-     * Hide the control view from the screen.
-     */
-    public void hide() {
-        mProvider.hide_impl();
-    }
-
-    /**
      * Changes the visibility state of an individual button. Default value is View.Visible.
      *
      * @param button the {@code Button} assigned to individual buttons
@@ -217,75 +207,34 @@
         mProvider.setButtonVisibility_impl(button, visibility);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        mProvider.onAttachedToWindow_impl();
+    /**
+     *  Requests focus for the play/pause button.
+     */
+    public void requestPlayButtonFocus() {
+        mProvider.requestPlayButtonFocus_impl();
+    }
+
+    /**
+     * Sets a new timeout value (in milliseconds) for showing MediaControlView2. The default value
+     * is set as 2 seconds.
+     * @param timeout the
+     */
+    public void setTimeout(long timeout) {
+        mProvider.setTimeout_impl(timeout);
+    }
+
+    /**
+     * Retrieves current timeout value (in milliseconds) for showing MediaControlView2. The default
+     * value is set as 2 seconds.
+     */
+    public long getTimeout() {
+        return mProvider.getTimeout_impl();
     }
 
     @Override
-    protected void onDetachedFromWindow() {
-        mProvider.onDetachedFromWindow_impl();
-    }
+    // TODO Move this method to ViewProvider
+    public void onVisibilityAggregated(boolean isVisible) {
 
-    @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);
-        }
+        mProvider.onVisibilityAggregated_impl(isVisible);
     }
 }
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 ac83313..78ca011 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -27,12 +27,11 @@
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.media.update.ApiLoader;
+import android.media.update.FrameLayoutHelper;
 import android.media.update.VideoView2Provider;
-import android.media.update.ViewProvider;
 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 FrameLayoutHelper<VideoView2Provider> {
     /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
@@ -112,21 +111,19 @@
     public @interface ViewType {}
 
     /**
-     * Indicates video is rendering on SurfaceView
+     * Indicates video is rendering on SurfaceView.
      *
      * @see #setViewType
      */
     public static final int VIEW_TYPE_SURFACEVIEW = 1;
 
     /**
-     * Indicates video is rendering on TextureView
+     * Indicates video is rendering on TextureView.
      *
      * @see #setViewType
      */
     public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
-    private final VideoView2Provider mProvider;
-
     public VideoView2(@NonNull Context context) {
         this(context, null);
     }
@@ -142,17 +139,10 @@
     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) ->
+                ApiLoader.getProvider(context).createVideoView2(
+                        (VideoView2) instance, superProvider, attrs, defStyleAttr, defStyleRes),
+                context, attrs, defStyleAttr, defStyleRes);
     }
 
     /**
@@ -188,18 +178,12 @@
     }
 
     /**
-     * Starts rendering closed caption or subtitles if there is any. The first subtitle track will
-     * be chosen by default if there multiple subtitle tracks exist.
+     * Shows or hides closed caption or subtitles if there is any.
+     * The first subtitle track will be chosen by default if there multiple subtitle tracks exist.
+     * @param show shows closed caption or subtitles if this value is true, or hides.
      */
-    public void showSubtitle() {
-        mProvider.showSubtitle_impl();
-    }
-
-    /**
-     * Stops showing closed captions or subtitles.
-     */
-    public void hideSubtitle() {
-        mProvider.hideSubtitle_impl();
+    public void showSubtitle(boolean show) {
+        mProvider.showSubtitle_impl(show);
     }
 
     /**
@@ -503,76 +487,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/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 1dfff5e..2ab2d20 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -103,14 +103,11 @@
         List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
         for (int i = 0, N = intents.size(); i < N; i++) {
             final Intent intent = intents.get(i);
-            int flags = PackageManager.MATCH_DEFAULT_ONLY
-                    | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
-                    | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
-            if (intent.isBrowsableWebIntent()
-                        || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
-                flags |= PackageManager.MATCH_INSTANT;
-            }
-            final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
+            final List<ResolveInfo> infos = mpm.queryIntentActivities(intent,
+                    PackageManager.MATCH_DEFAULT_ONLY
+                            | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
+                            | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0)
+                            | PackageManager.MATCH_INSTANT);
             // Remove any activities that are not exported.
             int totalSize = infos.size();
             for (int j = totalSize - 1; j >= 0 ; j--) {
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/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/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 0e562c0..c88cf5c 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -16,16 +16,16 @@
 
 #include "GraphicsJNI.h"
 #include "ImageDecoder.h"
-#include "core_jni_helpers.h"
 #include "Utils.h"
+#include "core_jni_helpers.h"
 
-#include <hwui/AnimatedImageDrawable.h>
-#include <hwui/Canvas.h>
 #include <SkAndroidCodec.h>
 #include <SkAnimatedImage.h>
 #include <SkColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include <hwui/AnimatedImageDrawable.h>
+#include <hwui/Canvas.h>
 
 using namespace android;
 
@@ -85,6 +85,9 @@
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
 }
 
+// Java's FINISHED relies on this being -1
+static_assert(SkAnimatedImage::kFinished == -1);
+
 static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                          jlong canvasPtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
@@ -115,9 +118,6 @@
     return drawable->isRunning();
 }
 
-// Java's NOT_RUNNING relies on this being -2.0.
-static_assert(SkAnimatedImage::kNotRunning == -2.0);
-
 static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
     return drawable->start();
@@ -172,6 +172,11 @@
     return sizeof(drawable);
 }
 
+static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    drawable->markInvisible();
+}
+
 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
     { "nCreate",             "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate },
     { "nGetNativeFinalizer", "()J",                                                          (void*) AnimatedImageDrawable_nGetNativeFinalizer },
@@ -185,6 +190,7 @@
     { "nSetLoopCount",       "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetLoopCount },
     { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
     { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
+    { "nMarkInvisible",      "(J)V",                                                         (void*) AnimatedImageDrawable_nMarkInvisible },
 };
 
 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
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_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index dddddbb..d33337d 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -72,8 +72,7 @@
     Paint* paint = toPaint(paintPtr);
     const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
     minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
-    toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint),
-                                       typeface->fFontCollection, isRtl);
+    toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl);
 }
 
 // Regular JNI
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 421e0de..f5c09fd 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -524,15 +524,15 @@
 static jint nativeSetSharedBufferModeEnabled(JNIEnv* env, jclass clazz, jlong nativeObject,
         jboolean enabled) {
     Surface* surface = reinterpret_cast<Surface*>(nativeObject);
-    return ((ANativeWindow*) nativeObject)->perform(surface,
-            NATIVE_WINDOW_SET_SHARED_BUFFER_MODE, enabled);
+    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+    return anw->perform(surface, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE, int(enabled));
 }
 
 static jint nativeSetAutoRefreshEnabled(JNIEnv* env, jclass clazz, jlong nativeObject,
         jboolean enabled) {
     Surface* surface = reinterpret_cast<Surface*>(nativeObject);
-    return ((ANativeWindow*) nativeObject)->perform(surface,
-            NATIVE_WINDOW_SET_AUTO_REFRESH, enabled);
+    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+    return anw->perform(surface, NATIVE_WINDOW_SET_AUTO_REFRESH, int(enabled));
 }
 
 namespace uirenderer {
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..e5ba6d7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2723,6 +2723,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 +2739,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> -->
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 5184dda..0543305 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8773,6 +8773,16 @@
         <attr name="fontProviderCerts" format="reference" />
     </declare-styleable>
 
+    <!-- Attributes that are read when parsing a  tag. -->
+    <declare-styleable name="VideoView2">
+        <attr name="enableControlView" format="boolean" />
+        <attr name="showSubtitle" format="boolean" />
+        <attr name="viewType" format="enum">
+            <enum name="surfaceView" value="0" />
+            <enum name="textureView" value="1" />
+        </attr>
+    </declare-styleable>
+
     <!-- @hide -->
     <declare-styleable name="RecyclerView">
         <attr name="layoutManager" format="string" />
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/symbols.xml b/core/res/res/values/symbols.xml
index 09d3121..a62d49e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3101,6 +3101,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 +3241,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/coretests/Android.mk b/core/tests/coretests/Android.mk
index 66731df..2ea1b46 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -53,6 +53,7 @@
     android.test.base \
     android.test.mock \
     framework-oahl-backward-compatibility \
+    framework-atb-backward-compatibility \
 
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
new file mode 100644
index 0000000..dce22ce
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link AndroidTestBaseUpdater}
+ */
+@SmallTest
+@RunWith(OptionalClassRunner.class)
+@OptionalClassRunner.OptionalClass("android.content.pm.AndroidTestBaseUpdater")
+public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O);
+
+        // Should add org.apache.http.legacy.
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_TEST_BASE);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(OTHER_LIBRARY);
+
+        // The org.apache.http.legacy jar should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-P.
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_TEST_BASE);
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesOptionalLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .optionalLibraries(ANDROID_TEST_BASE);
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
+        checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
index a305261..866de93 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
@@ -38,7 +38,7 @@
         PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_RUNNER);
 
         PackageBuilder after = builder()
-                .optionalLibraries(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
+                .optionalLibraries(ANDROID_TEST_MOCK, ANDROID_TEST_RUNNER);
 
         checkBackwardsCompatibility(before, after);
     }
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
index b26fc48..c64d520 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
@@ -17,10 +17,12 @@
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
+import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import android.os.Build;
 import android.support.test.filters.SmallTest;
 
@@ -56,8 +58,19 @@
     }
 
     /**
+     * Detect when the android.test.base is not on the bootclasspath.
+     *
+     * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
+     * succeed otherwise. This allows a developer to ensure that the tests are being
+     */
+    @Test
+    public void detectWhenATBisOnBCP() {
+        Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsATB());
+    }
+
+    /**
      * Ensures that the {@link PackageBackwardCompatibility} uses {@link OrgApacheHttpLegacyUpdater}
-     * when necessary.
+     * and {@link AndroidTestBaseUpdater} when necessary.
      *
      * <p>More comprehensive tests for that class can be found in
      * {@link OrgApacheHttpLegacyUpdaterTest}.
@@ -68,6 +81,9 @@
                 .targetSdkVersion(Build.VERSION_CODES.O);
 
         List<String> expected = new ArrayList<>();
+        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+            expected.add(ANDROID_TEST_BASE);
+        }
         if (!PackageBackwardCompatibility.bootClassPathContainsOAHL()) {
             expected.add(ORG_APACHE_HTTP_LEGACY);
         }
@@ -104,6 +120,30 @@
     }
 
     /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses
+     * {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+     * when necessary.
+     *
+     * <p>More comprehensive tests for that class can be found in
+     * {@link RemoveUnnecessaryAndroidTestBaseLibraryTest}.
+     */
+    @Test
+    public void android_test_base_in_usesLibraries() {
+        Assume.assumeTrue("Test requires that "
+                        + ANDROID_TEST_BASE + " is on the bootclasspath",
+                PackageBackwardCompatibility.bootClassPathContainsATB());
+
+        PackageBuilder before = builder()
+                .requiredLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    /**
      * Ensures that the {@link PackageBackwardCompatibility} uses a
      * {@link PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater}.
      *
@@ -114,8 +154,15 @@
     public void android_test_runner_in_usesLibraries() {
         PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_RUNNER);
 
+        List<String> expected = new ArrayList<>();
+        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+            expected.add(ANDROID_TEST_BASE);
+        }
+        expected.add(ANDROID_TEST_MOCK);
+        expected.add(ANDROID_TEST_RUNNER);
+
         PackageBuilder after = builder()
-                .requiredLibraries(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
+                .requiredLibraries(expected);
 
         checkBackwardsCompatibility(before, after);
     }
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
new file mode 100644
index 0000000..3dba440
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.content.pm;
+
+import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class RemoveUnnecessaryAndroidTestBaseLibraryTest
+        extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O);
+
+        // No change required.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(OTHER_LIBRARY);
+
+        // No change required.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesOptionalLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .optionalLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_bothLibraries() {
+        PackageBuilder before = builder()
+                .requiredLibraries(ANDROID_TEST_BASE)
+                .optionalLibraries(ANDROID_TEST_BASE);
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        PackageBuilder after = builder();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
+        // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
+        // PackageBackwardCompatibility and that seems to create a package-private lambda in
+        // android.content.pm which this then tries to reuse but fails because it cannot access
+        // package-private classes/members because the test is loaded by a different ClassLoader
+        // than the lambda.
+        checkBackwardsCompatibility(before, after,
+                () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
+    }
+
+}
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/etc/platform.xml b/data/etc/platform.xml
index 09192f4..04006b1 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -179,6 +179,8 @@
     <!-- This is a list of all the libraries available for application
          code to link against. -->
 
+    <library name="android.test.base"
+            file="/system/framework/android.test.base.jar" />
     <library name="android.test.mock"
             file="/system/framework/android.test.mock.jar" />
     <library name="android.test.runner"
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 0ec19f9..bd49b87 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -16,6 +16,8 @@
 
 package android.graphics.drawable;
 
+import dalvik.annotation.optimization.FastNative;
+
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -60,7 +62,6 @@
     private int mIntrinsicHeight;
 
     private boolean mStarting;
-    private boolean mRunning;
 
     private Handler mHandler;
 
@@ -222,8 +223,8 @@
         return mIntrinsicHeight;
     }
 
-    // nDraw returns -2 if the animation is not running.
-    private static final int NOT_RUNNING = -2;
+    // nDraw returns -1 if the animation has finished.
+    private static final int FINISHED = -1;
 
     @Override
     public void draw(@NonNull Canvas canvas) {
@@ -235,8 +236,6 @@
             mStarting = false;
 
             postOnAnimationStart();
-
-            mRunning = true;
         }
 
         long nextUpdate = nDraw(mState.mNativePtr, canvas.getNativeCanvasWrapper());
@@ -244,12 +243,9 @@
         // will manage the animation
         if (nextUpdate > 0) {
             scheduleSelf(mRunnable, nextUpdate);
-        } else if (nextUpdate == NOT_RUNNING) {
-            // -2 means the animation ended, when drawn in software mode.
-            if (mRunning) {
-                postOnAnimationEnd();
-                mRunning = false;
-            }
+        } else if (nextUpdate == FINISHED) {
+            // This means the animation was drawn in software mode and ended.
+            postOnAnimationEnd();
         }
     }
 
@@ -292,6 +288,19 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        if (!super.setVisible(visible, restart)) {
+            return false;
+        }
+
+        if (!visible) {
+            nMarkInvisible(mState.mNativePtr);
+        }
+
+        return true;
+    }
+
     // Animatable overrides
     /**
      *  Return whether the animation is currently running.
@@ -301,13 +310,17 @@
      */
     @Override
     public boolean isRunning() {
-        return mRunning;
+        if (mState == null) {
+            throw new IllegalStateException("called isRunning on empty AnimatedImageDrawable");
+        }
+        return nIsRunning(mState.mNativePtr);
     }
 
     /**
      *  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>
@@ -336,7 +349,6 @@
             throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
         }
         nStop(mState.mNativePtr);
-        mRunning = false;
     }
 
     // Animatable2 overrides
@@ -405,18 +417,29 @@
     private static native long nCreate(long nativeImageDecoder,
             @Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
         throws IOException;
+    @FastNative
     private static native long nGetNativeFinalizer();
     private static native long nDraw(long nativePtr, long canvasNativePtr);
+    @FastNative
     private static native void nSetAlpha(long nativePtr, int alpha);
+    @FastNative
     private static native int nGetAlpha(long nativePtr);
+    @FastNative
     private static native void nSetColorFilter(long nativePtr, long nativeFilter);
+    @FastNative
     private static native boolean nIsRunning(long nativePtr);
     // Return whether the animation started.
+    @FastNative
     private static native boolean nStart(long nativePtr);
+    @FastNative
     private static native void nStop(long nativePtr);
+    @FastNative
     private static native void nSetLoopCount(long nativePtr, int loopCount);
     // Pass the drawable down to native so it can call onAnimationEnd.
     private static native void nSetOnAnimationEndListener(long nativePtr,
             @Nullable AnimatedImageDrawable drawable);
+    @FastNative
     private static native long nNativeByteSize(long nativePtr);
+    @FastNative
+    private static native void nMarkInvisible(long nativePtr);
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index c429fd3..ded427e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -618,9 +618,9 @@
      * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
      * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
      */
-    public int addAuthToken(byte[] authToken, int userId) {
+    public int addAuthToken(byte[] authToken) {
         try {
-            return mBinder.addAuthToken(authToken, userId);
+            return mBinder.addAuthToken(authToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 419eb24..09b3b9b 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,7 +243,13 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
+                        spec.isUserAuthenticationRequired(),
+                        spec.getUserAuthenticationValidityDurationSeconds(),
+                        spec.isUserAuthenticationValidWhileOnBody(),
+                        spec.isInvalidatedByBiometricEnrollment(),
+                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                        spec.isUserConfirmationRequired());
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -279,7 +285,16 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args, spec);
+        KeymasterUtils.addUserAuthArgs(args,
+                spec.isUserAuthenticationRequired(),
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                spec.isUserAuthenticationValidWhileOnBody(),
+                spec.isInvalidatedByBiometricEnrollment(),
+                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                spec.isUserConfirmationRequired());
+        if (spec.isTrustedUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d68a33d..e33e3cd 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,7 +344,13 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
+                        mSpec.isUserAuthenticationRequired(),
+                        mSpec.getUserAuthenticationValidityDurationSeconds(),
+                        mSpec.isUserAuthenticationValidWhileOnBody(),
+                        mSpec.isInvalidatedByBiometricEnrollment(),
+                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+                        mSpec.isUserConfirmationRequired());
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -535,7 +541,13 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args, mSpec);
+        KeymasterUtils.addUserAuthArgs(args,
+                mSpec.isUserAuthenticationRequired(),
+                mSpec.getUserAuthenticationValidityDurationSeconds(),
+                mSpec.isUserAuthenticationValidWhileOnBody(),
+                mSpec.isInvalidatedByBiometricEnrollment(),
+                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 fc86ca0..05cc74a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,7 +497,13 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs, spec);
+                KeymasterUtils.addUserAuthArgs(importArgs,
+                        spec.isUserAuthenticationRequired(),
+                        spec.getUserAuthenticationValidityDurationSeconds(),
+                        spec.isUserAuthenticationValidWhileOnBody(),
+                        spec.isInvalidatedByBiometricEnrollment(),
+                        spec.getBoundToSpecificSecureUserId(),
+                        spec.isUserConfirmationRequired());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -694,7 +700,13 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args, params);
+            KeymasterUtils.addUserAuthArgs(args,
+                    params.isUserAuthenticationRequired(),
+                    params.getUserAuthenticationValidityDurationSeconds(),
+                    params.isUserAuthenticationValidWhileOnBody(),
+                    params.isInvalidatedByBiometricEnrollment(),
+                    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 0291b8a..da23c70 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
-import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -233,7 +232,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -265,7 +264,7 @@
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
-    private final boolean mUnlockedDeviceRequired;
+    private final boolean mUserConfirmationRequired;
 
     /**
      * @hide should be built with Builder
@@ -296,7 +295,7 @@
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             boolean isStrongBoxBacked,
-            boolean unlockedDeviceRequired) {
+            boolean userConfirmationRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -344,7 +343,7 @@
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
-        mUnlockedDeviceRequired = unlockedDeviceRequired;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -551,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()}).
@@ -650,22 +669,6 @@
     }
 
     /**
-     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
-     *
-     * @see Builder#SetUnlockedDeviceRequired(boolean)
-     */
-    public boolean isUnlockedDeviceRequired() {
-        return mUnlockedDeviceRequired;
-    }
-
-    /**
-     * @hide
-     */
-    public long getBoundToSpecificSecureUserId() {
-        return GateKeeper.INVALID_SECURE_USER_ID;
-    }
-
-    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -695,7 +698,7 @@
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
-        private boolean mUnlockedDeviceRequired = false;
+        private boolean mUserConfirmationRequired;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1084,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)}).
@@ -1241,18 +1267,6 @@
         }
 
         /**
-         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
-         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
-         * while the screen is locked will fail. A locked device requires a PIN, password,
-         * fingerprint, or other trusted factor to access.
-         */
-        @NonNull
-        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
-            mUnlockedDeviceRequired = unlockedDeviceRequired;
-            return this;
-        }
-
-        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1283,7 +1297,7 @@
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mIsStrongBoxBacked,
-                    mUnlockedDeviceRequired);
+                    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 a5b85ce..b5b3281 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
+public final class KeyProtection implements ProtectionParameter {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -228,8 +228,7 @@
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
-    private final boolean mTrustedUserPresenceRequired;
-    private final boolean mUnlockedDeviceRequired;
+    private final boolean mUserConfirmationRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -243,12 +242,11 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
-            boolean trustedUserPresenceRequired,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
             boolean criticalToDeviceEncryption,
-            boolean unlockedDeviceRequired) {
+            boolean userConfirmationRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -266,8 +264,7 @@
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
-        mTrustedUserPresenceRequired = trustedUserPresenceRequired;
-        mUnlockedDeviceRequired = unlockedDeviceRequired;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -402,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()}).
@@ -420,14 +437,6 @@
     }
 
     /**
-     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
-     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
-     */
-    public boolean isTrustedUserPresenceRequired() {
-        return mTrustedUserPresenceRequired;
-    }
-
-    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -485,15 +494,6 @@
     }
 
     /**
-     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
-     *
-     * @see Builder#SetRequireDeviceUnlocked(boolean)
-     */
-    public boolean isUnlockedDeviceRequired() {
-        return mUnlockedDeviceRequired;
-    }
-
-    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -511,9 +511,7 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
-        private boolean mTrustedUserPresenceRequired = false;
-        private boolean mUnlockedDeviceRequired = false;
-
+        private boolean mUserConfirmationRequired;
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -745,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)}).
@@ -790,16 +811,6 @@
         }
 
         /**
-         * Sets whether a test of user presence is required to be performed between the
-         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
-         */
-        @NonNull
-        public Builder setTrustedUserPresenceRequired(boolean required) {
-            mTrustedUserPresenceRequired = required;
-            return this;
-        }
-
-        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -881,18 +892,6 @@
         }
 
         /**
-         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
-         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
-         * while the screen is locked will fail. A locked device requires a PIN, password,
-         * fingerprint, or other trusted factor to access.
-         */
-        @NonNull
-        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
-            mUnlockedDeviceRequired = unlockedDeviceRequired;
-            return this;
-        }
-
-        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -911,12 +910,11 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
-                    mTrustedUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
                     mCriticalToDeviceEncryption,
-                    mUnlockedDeviceRequired);
+                    mUserConfirmationRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index eb6a2a2..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,28 +94,29 @@
      *        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.
      */
     public static void addUserAuthArgs(KeymasterArguments args,
-            UserAuthArgs spec) {
-        args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, 0);
-
-        if (spec.isTrustedUserPresenceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+            boolean userAuthenticationRequired,
+            int userAuthenticationValidityDurationSeconds,
+            boolean userAuthenticationValidWhileOnBody,
+            boolean invalidatedByBiometricEnrollment,
+            long boundToSpecificSecureUserId,
+            boolean userConfirmationRequired) {
+        if (userConfirmationRequired) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
         }
 
-        if (spec.isUnlockedDeviceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
-        }
-
-        if (!spec.isUserAuthenticationRequired()) {
+        if (!userAuthenticationRequired) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
+        if (userAuthenticationValidityDurationSeconds == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -130,9 +132,9 @@
             }
 
             long sid;
-            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = spec.getBoundToSpecificSecureUserId();
-            } else if (spec.isInvalidatedByBiometricEnrollment()) {
+            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = boundToSpecificSecureUserId;
+            } else if (invalidatedByBiometricEnrollment) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -145,14 +147,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (spec.isUserAuthenticationValidWhileOnBody()) {
+            if (userAuthenticationValidWhileOnBody) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = spec.getBoundToSpecificSecureUserId();
+            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = boundToSpecificSecureUserId;
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -163,8 +165,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    spec.getUserAuthenticationValidityDurationSeconds());
-            if (spec.isUserAuthenticationValidWhileOnBody()) {
+                    userAuthenticationValidityDurationSeconds);
+            if (userAuthenticationValidWhileOnBody) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
deleted file mode 100644
index 6fb3486..0000000
--- a/keystore/java/android/security/keystore/UserAuthArgs.java
+++ /dev/null
@@ -1,37 +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.security.keystore;
-
-/**
- * @hide
- *
- * This is an interface to encapsulate the user authentication arguments that
- * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
- * authorization characteristics for new or imported keys can implement this
- * interface to be passed to that method.
- */
-public interface UserAuthArgs {
-
-    boolean isUserAuthenticationRequired();
-    int getUserAuthenticationValidityDurationSeconds();
-    boolean isUserAuthenticationValidWhileOnBody();
-    boolean isInvalidatedByBiometricEnrollment();
-    boolean isTrustedUserPresenceRequired();
-    boolean isUnlockedDeviceRequired();
-    long getBoundToSpecificSecureUserId();
-
-}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17f9b7c..3323bce 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -138,6 +138,7 @@
 
     srcs: [
         "hwui/AnimatedImageDrawable.cpp",
+        "hwui/AnimatedImageThread.cpp",
         "hwui/Bitmap.cpp",
         "font/CacheTexture.cpp",
         "font/Font.cpp",
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index e01bf3d..5356d3b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -15,21 +15,21 @@
  */
 
 #include "AnimatedImageDrawable.h"
+#include "AnimatedImageThread.h"
 
-#include "thread/Task.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
 #include "utils/TraceUtils.h"
 
 #include <SkPicture.h>
 #include <SkRefCnt.h>
-#include <SkTime.h>
 #include <SkTLazy.h>
+#include <SkTime.h>
 
 namespace android {
 
 AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage)
-    : mSkAnimatedImage(std::move(animatedImage)) { }
+        : mSkAnimatedImage(std::move(animatedImage)) {
+    mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
+}
 
 void AnimatedImageDrawable::syncProperties() {
     mAlpha = mStagingAlpha;
@@ -37,88 +37,81 @@
 }
 
 bool AnimatedImageDrawable::start() {
-    SkAutoExclusive lock(mLock);
-    if (mSkAnimatedImage->isRunning()) {
+    if (mRunning) {
         return false;
     }
 
-    if (!mSnapshot) {
-        mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
-    }
+    // This will trigger a reset.
+    mFinished = true;
 
-    // While stopped, update() does not decode, but it does advance the time.
-    // This prevents us from skipping ahead when we resume.
-    const double currentTime = SkTime::GetMSecs();
-    mSkAnimatedImage->update(currentTime);
-    mSkAnimatedImage->start();
-    return mSkAnimatedImage->isRunning();
+    mRunning = true;
+    return true;
 }
 
 void AnimatedImageDrawable::stop() {
-    SkAutoExclusive lock(mLock);
-    mSkAnimatedImage->stop();
+    mRunning = false;
 }
 
 bool AnimatedImageDrawable::isRunning() {
-    return mSkAnimatedImage->isRunning();
+    return mRunning;
 }
 
-// This is really a Task<void> but that doesn't really work when Future<>
-// expects to be able to get/set a value
-class AnimatedImageDrawable::AnimatedImageTask : public uirenderer::Task<bool> {
-public:
-    AnimatedImageTask(AnimatedImageDrawable* animatedImageDrawable)
-            : mAnimatedImageDrawable(sk_ref_sp(animatedImageDrawable)) {}
-
-    sk_sp<AnimatedImageDrawable> mAnimatedImageDrawable;
-    bool mIsCompleted = false;
-};
-
-class AnimatedImageDrawable::AnimatedImageTaskProcessor : public uirenderer::TaskProcessor<bool> {
-public:
-    explicit AnimatedImageTaskProcessor(uirenderer::TaskManager* taskManager)
-            : uirenderer::TaskProcessor<bool>(taskManager) {}
-    ~AnimatedImageTaskProcessor() {}
-
-    virtual void onProcess(const sp<uirenderer::Task<bool>>& task) override {
-        ATRACE_NAME("Updating AnimatedImageDrawables");
-        AnimatedImageTask* t = static_cast<AnimatedImageTask*>(task.get());
-        t->mAnimatedImageDrawable->update();
-        t->mIsCompleted = true;
-        task->setResult(true);
-    };
-};
-
-void AnimatedImageDrawable::scheduleUpdate(uirenderer::TaskManager* taskManager) {
-    if (!mSkAnimatedImage->isRunning()
-            || (mDecodeTask.get() != nullptr && !mDecodeTask->mIsCompleted)) {
-        return;
-    }
-
-    if (!mDecodeTaskProcessor.get()) {
-        mDecodeTaskProcessor = new AnimatedImageTaskProcessor(taskManager);
-    }
-
-    // TODO get one frame ahead and only schedule updates when you need to replenish
-    mDecodeTask = new AnimatedImageTask(this);
-    mDecodeTaskProcessor->add(mDecodeTask);
+bool AnimatedImageDrawable::nextSnapshotReady() const {
+    return mNextSnapshot.valid() &&
+           mNextSnapshot.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
 }
 
-void AnimatedImageDrawable::update() {
-    SkAutoExclusive lock(mLock);
-
-    if (!mSkAnimatedImage->isRunning()) {
-        return;
-    }
-
+// Only called on the RenderThread while UI thread is locked.
+bool AnimatedImageDrawable::isDirty() {
     const double currentTime = SkTime::GetMSecs();
-    if (currentTime >= mNextFrameTime) {
-        mNextFrameTime = mSkAnimatedImage->update(currentTime);
-        mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
-        mIsDirty = true;
+    const double lastWallTime = mLastWallTime;
+
+    mLastWallTime = currentTime;
+    if (!mRunning) {
+        mDidDraw = false;
+        return false;
     }
+
+    std::unique_lock lock{mSwapLock};
+    if (mDidDraw) {
+        mCurrentTime += currentTime - lastWallTime;
+        mDidDraw = false;
+    }
+
+    if (!mNextSnapshot.valid()) {
+        // Need to trigger onDraw in order to start decoding the next frame.
+        return true;
+    }
+
+    return nextSnapshotReady() && mCurrentTime >= mTimeToShowNextSnapshot;
 }
 
+// Only called on the AnimatedImageThread.
+AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
+    Snapshot snap;
+    {
+        std::unique_lock lock{mImageLock};
+        snap.mDuration = mSkAnimatedImage->decodeNextFrame();
+        snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+    }
+
+    return snap;
+}
+
+// Only called on the AnimatedImageThread.
+AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
+    Snapshot snap;
+    {
+        std::unique_lock lock{mImageLock};
+        mSkAnimatedImage->reset();
+        snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+        snap.mDuration = mSkAnimatedImage->currentFrameDuration();
+    }
+
+    return snap;
+}
+
+// Only called on the RenderThread.
 void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
     SkTLazy<SkPaint> lazyPaint;
     if (mAlpha != SK_AlphaOPAQUE || mColorFilter.get()) {
@@ -128,25 +121,71 @@
         lazyPaint.get()->setFilterQuality(kLow_SkFilterQuality);
     }
 
-    SkAutoExclusive lock(mLock);
-    if (mSnapshot) {
-        canvas->drawPicture(mSnapshot, nullptr, lazyPaint.getMaybeNull());
-    } else {
-        // TODO: we could potentially keep the cached surface around if there is a paint and we know
-        // the drawable is attached to the view system
+    mDidDraw = true;
+
+    bool drewDirectly = false;
+    if (!mSnapshot.mPic) {
+        // The image is not animating, and never was. Draw directly from
+        // mSkAnimatedImage.
         SkAutoCanvasRestore acr(canvas, false);
         if (lazyPaint.isValid()) {
             canvas->saveLayer(mSkAnimatedImage->getBounds(), lazyPaint.get());
         }
+
+        std::unique_lock lock{mImageLock};
         mSkAnimatedImage->draw(canvas);
+        drewDirectly = true;
     }
 
-    mIsDirty = false;
+    if (mRunning && mFinished) {
+        auto& thread = uirenderer::AnimatedImageThread::getInstance();
+        mNextSnapshot = thread.reset(sk_ref_sp(this));
+        mFinished = false;
+    }
+
+    bool finalFrame = false;
+    if (mRunning && nextSnapshotReady()) {
+        std::unique_lock lock{mSwapLock};
+        if (mCurrentTime >= mTimeToShowNextSnapshot) {
+            mSnapshot = mNextSnapshot.get();
+            const double timeToShowCurrentSnap = mTimeToShowNextSnapshot;
+            if (mSnapshot.mDuration == SkAnimatedImage::kFinished) {
+                finalFrame = true;
+                mRunning = false;
+                mFinished = true;
+            } else {
+                mTimeToShowNextSnapshot += mSnapshot.mDuration;
+                if (mCurrentTime >= mTimeToShowNextSnapshot) {
+                    // This would mean showing the current frame very briefly. It's
+                    // possible that not being displayed for a time resulted in
+                    // mCurrentTime being far ahead. Prevent showing many frames
+                    // rapidly by going back to the beginning of this frame time.
+                    mCurrentTime = timeToShowCurrentSnap;
+                }
+            }
+        }
+    }
+
+    if (mRunning && !mNextSnapshot.valid()) {
+        auto& thread = uirenderer::AnimatedImageThread::getInstance();
+        mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this));
+    }
+
+    if (!drewDirectly) {
+        // No other thread will modify mCurrentSnap so this should be safe to
+        // use without locking.
+        canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint.getMaybeNull());
+    }
+
+    if (finalFrame) {
+        if (mEndListener) {
+            mEndListener->onAnimationEnd();
+            mEndListener = nullptr;
+        }
+    }
 }
 
 double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
-    // update the drawable with the current time
-    double nextUpdate = mSkAnimatedImage->update(SkTime::GetMSecs());
     SkAutoCanvasRestore acr(canvas, false);
     if (mStagingAlpha != SK_AlphaOPAQUE || mStagingColorFilter.get()) {
         SkPaint paint;
@@ -154,8 +193,59 @@
         paint.setColorFilter(mStagingColorFilter);
         canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint);
     }
-    canvas->drawDrawable(mSkAnimatedImage.get());
-    return nextUpdate;
+
+    if (mFinished && !mRunning) {
+        // Continue drawing the last frame, and return 0 to indicate no need to
+        // redraw.
+        std::unique_lock lock{mImageLock};
+        canvas->drawDrawable(mSkAnimatedImage.get());
+        return 0.0;
+    }
+
+    bool update = false;
+    {
+        const double currentTime = SkTime::GetMSecs();
+        std::unique_lock lock{mSwapLock};
+        // mLastWallTime starts off at 0. If it is still 0, just update it to
+        // the current time and avoid updating
+        if (mLastWallTime == 0.0) {
+            mCurrentTime = currentTime;
+        } else if (mRunning) {
+            if (mFinished) {
+                mCurrentTime = currentTime;
+                {
+                    std::unique_lock lock{mImageLock};
+                    mSkAnimatedImage->reset();
+                }
+                mTimeToShowNextSnapshot = currentTime + mSkAnimatedImage->currentFrameDuration();
+            } else {
+                mCurrentTime += currentTime - mLastWallTime;
+                update = mCurrentTime >= mTimeToShowNextSnapshot;
+            }
+        }
+        mLastWallTime = currentTime;
+    }
+
+    double duration = 0.0;
+    {
+        std::unique_lock lock{mImageLock};
+        if (update) {
+            duration = mSkAnimatedImage->decodeNextFrame();
+        }
+
+        canvas->drawDrawable(mSkAnimatedImage.get());
+    }
+
+    std::unique_lock lock{mSwapLock};
+    if (update) {
+        if (duration == SkAnimatedImage::kFinished) {
+            mRunning = false;
+            mFinished = true;
+        } else {
+            mTimeToShowNextSnapshot += duration;
+        }
+    }
+    return mTimeToShowNextSnapshot;
 }
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index df8a5e4..9d84ed5 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -17,22 +17,20 @@
 #pragma once
 
 #include <cutils/compiler.h>
+#include <utils/Macros.h>
 #include <utils/RefBase.h>
 
 #include <SkAnimatedImage.h>
 #include <SkCanvas.h>
 #include <SkColorFilter.h>
 #include <SkDrawable.h>
-#include <SkMutex.h>
+#include <SkPicture.h>
 
-class SkPicture;
+#include <future>
+#include <mutex>
 
 namespace android {
 
-namespace uirenderer {
-class TaskManager;
-}
-
 class OnAnimationEndListener {
 public:
     virtual ~OnAnimationEndListener() {}
@@ -41,68 +39,102 @@
 };
 
 /**
- * Native component of android.graphics.drawable.AnimatedImageDrawables.java.  This class can be
- * drawn into Canvas.h and maintains the state needed to drive the animation from the RenderThread.
+ * Native component of android.graphics.drawable.AnimatedImageDrawables.java.
+ * This class can be drawn into Canvas.h and maintains the state needed to drive
+ * the animation from the RenderThread.
  */
 class ANDROID_API AnimatedImageDrawable : public SkDrawable {
 public:
     AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage);
 
     /**
-     * This returns true if the animation has updated and signals that the next draw will contain
-     * new content.
+     * This updates the internal time and returns true if the animation needs
+     * to be redrawn.
+     *
+     * This is called on RenderThread, while the UI thread is locked.
      */
-    bool isDirty() const { return mIsDirty; }
+    bool isDirty();
 
     int getStagingAlpha() const { return mStagingAlpha; }
     void setStagingAlpha(int alpha) { mStagingAlpha = alpha; }
     void setStagingColorFilter(sk_sp<SkColorFilter> filter) { mStagingColorFilter = filter; }
     void syncProperties();
 
-    virtual SkRect onGetBounds() override {
-        return mSkAnimatedImage->getBounds();
-    }
+    virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
 
+    // Draw to software canvas, and return time to next draw.
     double drawStaging(SkCanvas* canvas);
 
-    // Returns true if the animation was started; false otherwise (e.g. it was already running)
+    // Returns true if the animation was started; false otherwise (e.g. it was
+    // already running)
     bool start();
     void stop();
     bool isRunning();
-    void setRepetitionCount(int count) {
-        mSkAnimatedImage->setRepetitionCount(count);
-    }
+    void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
 
     void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
         mEndListener = std::move(listener);
     }
 
-    void scheduleUpdate(uirenderer::TaskManager* taskManager);
+    void markInvisible() { mDidDraw = false; }
+
+    struct Snapshot {
+        sk_sp<SkPicture> mPic;
+        int mDuration;
+
+        Snapshot() = default;
+
+        Snapshot(Snapshot&&) = default;
+        Snapshot& operator=(Snapshot&&) = default;
+
+        PREVENT_COPY_AND_ASSIGN(Snapshot);
+    };
+
+    // These are only called on AnimatedImageThread.
+    Snapshot decodeNextFrame();
+    Snapshot reset();
 
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
-    void update();
-
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
-    sk_sp<SkPicture> mSnapshot;
-    SkMutex mLock;
+    bool mRunning = false;
+    bool mFinished = false;
+
+    // A snapshot of the current frame to draw.
+    Snapshot mSnapshot;
+
+    std::future<Snapshot> mNextSnapshot;
+
+    bool nextSnapshotReady() const;
+
+    // When to switch from mSnapshot to mNextSnapshot.
+    double mTimeToShowNextSnapshot = 0.0;
+
+    // The current time for the drawable itself.
+    double mCurrentTime = 0.0;
+
+    // The wall clock of the last time we called isDirty.
+    double mLastWallTime = 0.0;
+
+    // Whether we drew since the last call to isDirty.
+    bool mDidDraw = false;
+
+    // Locked when assigning snapshots and times. Operations while this is held
+    // should be short.
+    std::mutex mSwapLock;
+
+    // Locked when mSkAnimatedImage is being updated or drawn.
+    std::mutex mImageLock;
 
     int mStagingAlpha = SK_AlphaOPAQUE;
     sk_sp<SkColorFilter> mStagingColorFilter;
 
     int mAlpha = SK_AlphaOPAQUE;
     sk_sp<SkColorFilter> mColorFilter;
-    double mNextFrameTime = 0.0;
-    bool mIsDirty = false;
-
-    class AnimatedImageTask;
-    class AnimatedImageTaskProcessor;
-    sp<AnimatedImageTask> mDecodeTask;
-    sp<AnimatedImageTaskProcessor> mDecodeTaskProcessor;
 
     std::unique_ptr<OnAnimationEndListener> mEndListener;
 };
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageThread.cpp b/libs/hwui/hwui/AnimatedImageThread.cpp
new file mode 100644
index 0000000..c899003
--- /dev/null
+++ b/libs/hwui/hwui/AnimatedImageThread.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "AnimatedImageThread.h"
+
+#include <sys/resource.h>
+
+namespace android {
+namespace uirenderer {
+
+AnimatedImageThread& AnimatedImageThread::getInstance() {
+    static AnimatedImageThread* sInstance = new AnimatedImageThread();
+    return *sInstance;
+}
+
+AnimatedImageThread::AnimatedImageThread() {
+    setpriority(PRIO_PROCESS, 0, PRIORITY_NORMAL + PRIORITY_MORE_FAVORABLE);
+    start("AnimatedImageThread");
+}
+
+std::future<AnimatedImageDrawable::Snapshot> AnimatedImageThread::decodeNextFrame(
+        const sk_sp<AnimatedImageDrawable>& drawable) {
+    return queue().async([drawable]() { return drawable->decodeNextFrame(); });
+}
+
+std::future<AnimatedImageDrawable::Snapshot> AnimatedImageThread::reset(
+        const sk_sp<AnimatedImageDrawable>& drawable) {
+    return queue().async([drawable]() { return drawable->reset(); });
+}
+
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageThread.h b/libs/hwui/hwui/AnimatedImageThread.h
new file mode 100644
index 0000000..9e35374
--- /dev/null
+++ b/libs/hwui/hwui/AnimatedImageThread.h
@@ -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.
+ */
+
+#ifndef ANIMATEDIMAGETHREAD_H_
+#define ANIMATEDIMAGETHREAD_H_
+
+#include "AnimatedImageDrawable.h"
+#include "thread/ThreadBase.h"
+
+#include <SkRefCnt.h>
+
+namespace android {
+
+namespace uirenderer {
+
+class AnimatedImageThread : private ThreadBase {
+    PREVENT_COPY_AND_ASSIGN(AnimatedImageThread);
+
+public:
+    static AnimatedImageThread& getInstance();
+
+    std::future<AnimatedImageDrawable::Snapshot> decodeNextFrame(
+            const sk_sp<AnimatedImageDrawable>&);
+    std::future<AnimatedImageDrawable::Snapshot> reset(const sk_sp<AnimatedImageDrawable>&);
+
+private:
+    AnimatedImageThread();
+};
+
+}  // namespace uirenderer
+
+}  // namespace android
+
+#endif  // ANIMATEDIMAGETHREAD_H_
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index ba877d3..43f46ef 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -31,7 +31,7 @@
                                                         const Typeface* typeface) {
     const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
 
-    minikin::MinikinPaint minikinPaint;
+    minikin::MinikinPaint minikinPaint(resolvedFace->fFontCollection);
     /* Prepare minikin Paint */
     minikinPaint.size =
             paint->isLinearText() ? paint->getTextSize() : static_cast<int>(paint->getTextSize());
@@ -53,21 +53,20 @@
                                        size_t count, size_t bufSize, minikin::MeasuredText* mt,
                                        int mtOffset) {
     minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
-    const auto& fc = Typeface::resolveDefault(typeface)->fFontCollection;
     minikin::Layout layout;
 
     if (mt == nullptr) {
-        layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc);
+        layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
         return layout;
     }
 
     if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
                         minikin::Range(start, start + count),
-                        minikinPaint, fc, bidiFlags, mtOffset, &layout)) {
+                        minikinPaint, bidiFlags, mtOffset, &layout)) {
         return layout;
     }
 
-    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc);
+    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
     return layout;
 }
 
@@ -75,10 +74,8 @@
                                 const Typeface* typeface, const uint16_t* buf, size_t start,
                                 size_t count, size_t bufSize, float* advances) {
     minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
-    const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
     return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
-                                        resolvedTypeface->fFontCollection, advances,
-                                        nullptr /* extent */);
+                                        advances, nullptr /* extent */);
 }
 
 bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index cf0b6a4..aa14699 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -98,8 +98,6 @@
             isDirty = true;
         }
         if (animatedImage->isRunning()) {
-            static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
-                    ->scheduleDeferredUpdate(animatedImage);
             info.out.hasAnimations = true;
         }
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 534782a..9db39d9 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -40,7 +40,6 @@
 Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
 
 SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
-    mAnimatedImageDrawables.reserve(30);
     mVectorDrawables.reserve(30);
 }
 
@@ -327,15 +326,6 @@
 
     ATRACE_NAME("flush commands");
     surface->getCanvas()->flush();
-
-    // TODO move to another method
-    if (!mAnimatedImageDrawables.empty()) {
-        ATRACE_NAME("Update AnimatedImageDrawables");
-        for (auto animatedImage : mAnimatedImageDrawables) {
-            animatedImage->scheduleUpdate(getTaskManager());
-        }
-        mAnimatedImageDrawables.clear();
-    }
 }
 
 namespace {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index cc75e9c..3800194 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -55,12 +55,6 @@
 
     std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
 
-    void scheduleDeferredUpdate(AnimatedImageDrawable* imageDrawable) {
-        mAnimatedImageDrawables.push_back(imageDrawable);
-    }
-
-    std::vector<AnimatedImageDrawable*>* getAnimatingImages() { return &mAnimatedImageDrawables; }
-
     static void destroyLayer(RenderNode* node);
 
     static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
@@ -144,11 +138,6 @@
      */
     std::vector<VectorDrawableRoot*> mVectorDrawables;
 
-    /**
-     * populated by prepareTree with images with active animations
-     */
-    std::vector<AnimatedImageDrawable*> mAnimatedImageDrawables;
-
     // Block of properties used only for debugging to record a SkPicture and save it in a file.
     /**
      * mCapturedFile is used to enforce we don't capture more than once for a given name (cause
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index f5685d9..c2907a6 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -208,6 +208,11 @@
 Status
 DropBoxManager::addFile(const String16& tag, int fd, int flags)
 {
+    if (fd == -1) {
+        string message("invalid fd (-1) passed to to addFile");
+        ALOGW("DropboxManager: %s", message.c_str());
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
+    }
     Entry entry(tag, flags, fd);
     return add(entry);
 }
@@ -235,4 +240,3 @@
 }
 
 }} // namespace android::os
-
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/location/lib/Android.mk b/location/lib/Android.mk
index 62f5677..8424601 100644
--- a/location/lib/Android.mk
+++ b/location/lib/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.location.provider
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-subdir-java-files) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 include $(BUILD_JAVA_LIBRARY)
 
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/AudioManager.java b/media/java/android/media/AudioManager.java
index 7533701..22fa620 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -409,7 +409,7 @@
             ADJUST_TOGGLE_MUTE }
             )
     @Retention(RetentionPolicy.SOURCE)
-    public @interface VolumeAdjustement {}
+    public @interface VolumeAdjustment {}
 
     /** @hide */
     public static final String adjustToString(int adj) {
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 3d27a48..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;
@@ -104,7 +105,7 @@
          *
          * @param info new playback info
          */
-        public void onAudioInfoChanged(PlaybackInfo info) { }
+        public void onPlaybackInfoChanged(PlaybackInfo info) { }
 
         /**
          * Called when the allowed commands are changed by session.
@@ -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/MediaDrm.java b/media/java/android/media/MediaDrm.java
index a0edefa..90fcaab 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -942,43 +942,78 @@
             throws DeniedByServerException;
 
     /**
-     * A means of enforcing limits on the number of concurrent streams per subscriber
-     * across devices is provided via SecureStop. This is achieved by securely
-     * monitoring the lifetime of sessions.
+     * Secure stops are a way to enforce limits on the number of concurrent
+     * streams per subscriber across devices. They provide secure monitoring of
+     * the lifetime of content decryption keys in MediaDrm sessions.
      * <p>
-     * Information from the server related to the current playback session is written
-     * to persistent storage on the device when each MediaCrypto object is created.
+     * A secure stop is written to secure persistent memory when keys are loaded
+     * into a MediaDrm session. The secure stop state indicates that the keys
+     * are available for use. When playback completes and the keys are removed
+     * or the session is destroyed, the secure stop state is updated to indicate
+     * that keys are no longer usable.
      * <p>
-     * In the normal case, playback will be completed, the session destroyed and the
-     * Secure Stops will be queried. The app queries secure stops and forwards the
-     * secure stop message to the server which verifies the signature and notifies the
-     * server side database that the session destruction has been confirmed. The persisted
-     * record on the client is only removed after positive confirmation that the server
-     * received the message using releaseSecureStops().
+     * After playback, the app can query the secure stop and send it in a
+     * message to the license server confirming that the keys are no longer
+     * active. The license server returns a secure stop release response
+     * message to the app which then deletes the secure stop from persistent
+     * memory using {@link #releaseSecureStops}.
+     * @return a list of all secure stops from secure persistent memory
      */
     @NonNull
     public native List<byte[]> getSecureStops();
 
     /**
-     * Access secure stop by secure stop ID.
+     * Return a list of all secure stop IDs currently in persistent memory.
      *
-     * @param ssid - The secure stop ID provided by the license server.
+     * @return a list of secure stop IDs
+     */
+    @NonNull
+    public native List<byte[]> getSecureStopIds();
+
+    /**
+     * Access a specific secure stop given its secure stop ID.
+     *
+     * @param ssid the ID of the secure stop to return
+     * @return the secure stop identified by ssid
      */
     @NonNull
     public native byte[] getSecureStop(@NonNull byte[] ssid);
 
     /**
-     * Process the SecureStop server response message ssRelease.  After authenticating
-     * the message, remove the SecureStops identified in the response.
+     * Process the secure stop server response message ssRelease.  After
+     * authenticating the message, remove the secure stops identified in the
+     * response.
      *
      * @param ssRelease the server response indicating which secure stops to release
      */
     public native void releaseSecureStops(@NonNull byte[] ssRelease);
 
     /**
-     * Remove all secure stops without requiring interaction with the server.
+     * Remove a specific secure stop without requiring a secure stop release message
+     * from the license server.
+     * @param ssid the ID of the secure stop to remove
      */
-    public native void releaseAllSecureStops();
+    public native void removeSecureStop(@NonNull byte[] ssid);
+
+    /**
+     * Remove all secure stops without requiring a secure stop release message from
+     * the license server.
+     *
+     * This method was added in API 28. In API versions 18 through 27,
+     * {@link #releaseAllSecureStops} should be called instead. There is no need to
+     * do anything for API versions prior to 18.
+     */
+    public native void removeAllSecureStops();
+
+    /**
+     * Remove all secure stops without requiring a secure stop release message from
+     * the license server.
+     *
+     * @deprecated Remove all secure stops using {@link #removeAllSecureStops} instead.
+     */
+    public void releaseAllSecureStops() {
+        removeAllSecureStops();;
+    }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HDCP_LEVEL_UNKNOWN, HDCP_NONE, HDCP_V1, HDCP_V2,
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 94ada13..a901c68 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -19,6 +19,7 @@
 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;
@@ -63,26 +64,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;
         }
 
         /**
@@ -110,6 +101,11 @@
     }
 
     public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+
+        public MediaLibrarySessionCallback(Context context) {
+            super(context);
+        }
+
         /**
          * Called to get the root information for browsing by a particular client.
          * <p>
@@ -203,31 +199,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));
         }
     }
 
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/MediaSession2.java b/media/java/android/media/MediaSession2.java
index aa329ab..acf9615 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -30,19 +30,20 @@
 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.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;
 import android.os.IInterface;
-import android.os.Parcelable;
 import android.os.ResultReceiver;
 import android.text.TextUtils;
-import android.util.ArraySet;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -82,34 +83,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}.
@@ -120,43 +265,31 @@
      */
     // TODO(jaewan): Move this into the updatable.
     public static final class Command {
-        private static final String KEY_COMMAND_CODE
-                = "android.media.media_session2.command.command_code";
-        private static final String KEY_COMMAND_CUSTOM_COMMAND
-                = "android.media.media_session2.command.custom_command";
-        private static final String KEY_COMMAND_EXTRA
-                = "android.media.media_session2.command.extra";
+        private final CommandProvider mProvider;
 
-        private final int mCommandCode;
-        // Nonnull if it's custom command
-        private final String mCustomCommand;
-        private final Bundle mExtra;
-
-        public Command(int commandCode) {
-            mCommandCode = commandCode;
-            mCustomCommand = null;
-            mExtra = null;
+        public Command(@NonNull Context context, int commandCode) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2Command(this, commandCode, null, null);
         }
 
-        public Command(@NonNull String action, @Nullable Bundle extra) {
+        public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extra) {
             if (action == null) {
                 throw new IllegalArgumentException("action shouldn't be null");
             }
-            mCommandCode = COMMAND_CODE_CUSTOM;
-            mCustomCommand = action;
-            mExtra = extra;
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2Command(this, COMMAND_CODE_CUSTOM, action, extra);
         }
 
         public int getCommandCode() {
-            return mCommandCode;
+            return mProvider.getCommandCode_impl();
         }
 
         public @Nullable String getCustomCommand() {
-            return mCustomCommand;
+            return mProvider.getCustomCommand_impl();
         }
 
         public @Nullable Bundle getExtra() {
-            return mExtra;
+            return mProvider.getExtra_impl();
         }
 
         /**
@@ -164,28 +297,7 @@
          * @hide
          */
         public Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
-            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
-            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
-            return bundle;
-        }
-
-        /**
-         * @return a new Command instance from the Bundle
-         * @hide
-         */
-        public static Command fromBundle(Bundle command) {
-            int code = command.getInt(KEY_COMMAND_CODE);
-            if (code != COMMAND_CODE_CUSTOM) {
-                return new Command(code);
-            } else {
-                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
-                if (customCommand == null) {
-                    return null;
-                }
-                return new Command(customCommand, command.getBundle(KEY_COMMAND_EXTRA));
-            }
+            return mProvider.toBundle_impl();
         }
 
         @Override
@@ -193,74 +305,65 @@
             if (!(obj instanceof Command)) {
                 return false;
             }
-            Command other = (Command) obj;
-            // TODO(jaewan): Should we also compare contents in bundle?
-            //               It may not be possible if the bundle contains private class.
-            return mCommandCode == other.mCommandCode
-                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+            return mProvider.equals_impl(((Command) obj).mProvider);
         }
 
         @Override
         public int hashCode() {
-            final int prime = 31;
-            return ((mCustomCommand != null)
-                    ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
+            return mProvider.hashCode_impl();
+        }
+
+        /**
+         * @return a new Command instance from the Bundle
+         * @hide
+         */
+        public static Command fromBundle(@NonNull Context context, Bundle command) {
+            return ApiLoader.getProvider(context).fromBundle_MediaSession2Command(context, command);
         }
     }
 
     /**
      * Represent set of {@link Command}.
      */
-    // TODO(jaewan): Move this to updatable
     public static class CommandGroup {
-        private static final String KEY_COMMANDS =
-                "android.media.mediasession2.commandgroup.commands";
-        private ArraySet<Command> mCommands = new ArraySet<>();
+        private final CommandGroupProvider mProvider;
 
-        public CommandGroup() {
+        public CommandGroup(Context context) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2CommandGroup(context, this, null);
         }
 
-        public CommandGroup(CommandGroup others) {
-            mCommands.addAll(others.mCommands);
+        public CommandGroup(Context context, CommandGroup others) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2CommandGroup(context, this, others);
         }
 
         public void addCommand(Command command) {
-            mCommands.add(command);
+            mProvider.addCommand_impl(command);
         }
 
         public void addAllPredefinedCommands() {
-            // TODO(jaewan): Is there any better way than this?
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_START));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PAUSE));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PREPARE));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_FAST_FORWARD));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_REWIND));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SEEK_TO));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS));
+            mProvider.addAllPredefinedCommands_impl();
         }
 
         public void removeCommand(Command command) {
-            mCommands.remove(command);
+            mProvider.removeCommand_impl(command);
         }
 
         public boolean hasCommand(Command command) {
-            return mCommands.contains(command);
+            return mProvider.hasCommand_impl(command);
         }
 
         public boolean hasCommand(int code) {
-            if (code == COMMAND_CODE_CUSTOM) {
-                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
-            }
-            for (int i = 0; i < mCommands.size(); i++) {
-                if (mCommands.valueAt(i).getCommandCode() == code) {
-                    return true;
-                }
-            }
-            return false;
+            return mProvider.hasCommand_impl(code);
+        }
+
+        /**
+         * @hide
+         */
+        @SystemApi
+        public CommandGroupProvider getProvider() {
+            return mProvider;
         }
 
         /**
@@ -268,40 +371,16 @@
          * @hide
          */
         public Bundle toBundle() {
-            ArrayList<Bundle> list = new ArrayList<>();
-            for (int i = 0; i < mCommands.size(); i++) {
-                list.add(mCommands.valueAt(i).toBundle());
-            }
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(KEY_COMMANDS, list);
-            return bundle;
+            return mProvider.toBundle_impl();
         }
 
         /**
          * @return new instance of CommandGroup from the bundle
          * @hide
          */
-        public static @Nullable CommandGroup fromBundle(Bundle commands) {
-            if (commands == null) {
-                return null;
-            }
-            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
-            if (list == null) {
-                return null;
-            }
-            CommandGroup commandGroup = new CommandGroup();
-            for (int i = 0; i < list.size(); i++) {
-                Parcelable parcelable = list.get(i);
-                if (!(parcelable instanceof Bundle)) {
-                    continue;
-                }
-                Bundle commandBundle = (Bundle) parcelable;
-                Command command = Command.fromBundle(commandBundle);
-                if (command != null) {
-                    commandGroup.addCommand(command);
-                }
-            }
-            return commandGroup;
+        public static @Nullable CommandGroup fromBundle(Context context, Bundle commands) {
+            return ApiLoader.getProvider(context)
+                    .fromBundle_MediaSession2CommandGroup(context, commands);
         }
     }
 
@@ -313,6 +392,12 @@
      */
     // TODO(jaewan): Can we move this inside of the updatable for default implementation.
     public static class SessionCallback {
+        private final Context mContext;
+
+        public SessionCallback(Context context) {
+            mContext = context;
+        }
+
         /**
          * Called when a controller is created for this session. Return allowed commands for
          * controller. By default it allows all connection requests and commands.
@@ -325,7 +410,7 @@
          */
         // TODO(jaewan): Change return type. Once we do, null is for reject.
         public @Nullable CommandGroup onConnect(@NonNull ControllerInfo controller) {
-            CommandGroup commands = new CommandGroup();
+            CommandGroup commands = new CommandGroup(mContext);
             commands.addAllPredefinedCommands();
             return commands;
         }
@@ -338,22 +423,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
@@ -361,7 +460,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.
@@ -373,7 +473,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.
@@ -383,28 +524,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.
@@ -414,51 +568,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) { }
     };
 
     /**
@@ -467,36 +584,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);
         }
 
         /**
@@ -508,9 +600,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;
         }
 
         /**
@@ -526,9 +618,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;
         }
 
         /**
@@ -538,9 +630,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;
         }
 
         /**
@@ -553,12 +645,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;
         }
 
         /**
@@ -568,17 +657,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;
         }
 
         /**
@@ -588,7 +670,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();
+        }
     }
 
     /**
@@ -597,31 +681,18 @@
      * 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();
-            }
-            return new MediaSession2(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
-                    mSessionActivity, mCallbackExecutor, mCallback);
+            super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
+                    context, (Builder) instance, player));
         }
     }
 
     /**
      * Information of a controller.
      */
-    // TODO(jaewan): Move implementation to the updatable.
     public static final class ControllerInfo {
         private final ControllerInfoProvider mProvider;
 
@@ -633,7 +704,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);
         }
 
@@ -662,6 +733,9 @@
             return mProvider.isTrusted_impl();
         }
 
+        /**
+         * @hide
+         */
         @SystemApi
         public ControllerInfoProvider getProvider() {
             return mProvider;
@@ -788,9 +862,9 @@
          * @hide
          */
         // TODO(jaewan): @SystemApi
-        public static @Nullable CommandButton fromBundle(Bundle bundle) {
+        public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
             Builder builder = new Builder();
-            builder.setCommand(Command.fromBundle(bundle.getBundle(KEY_COMMAND)));
+            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));
@@ -860,7 +934,7 @@
     /**
      * Parameter for the playlist.
      */
-    public static class PlaylistParams {
+    public final static class PlaylistParams {
         /**
          * @hide
          */
@@ -915,79 +989,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();
         }
     }
 
@@ -1005,23 +1071,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/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/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/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index d0bba6d..4de731a 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -490,9 +490,9 @@
         public AudioPolicyVolumeCallback() {}
         /**
          * Called when volume key-related changes are triggered, on the key down event.
-         * @param adjustement the type of volume adjustment for the key.
+         * @param adjustment the type of volume adjustment for the key.
          */
-        public void onVolumeAdjustment(@AudioManager.VolumeAdjustement int adjustement) {}
+        public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
     }
 
     private void onPolicyStatusChange() {
@@ -625,6 +625,7 @@
                     } else { // should never be null, but don't crash
                         Log.e(TAG, "Invalid null volume event");
                     }
+                    break;
                 default:
                     Log.e(TAG, "Unknown event " + msg.what);
             }
diff --git a/media/java/android/media/update/FrameLayoutHelper.java b/media/java/android/media/update/FrameLayoutHelper.java
new file mode 100644
index 0000000..983dc70
--- /dev/null
+++ b/media/java/android/media/update/FrameLayoutHelper.java
@@ -0,0 +1,128 @@
+/*
+ * 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.widget.FrameLayout;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewProvider
+ *
+ * @hide
+ */
+public abstract class FrameLayoutHelper<T extends ViewProvider> extends FrameLayout {
+    /** @hide */
+    final public T mProvider;
+
+    /** @hide */
+    public FrameLayoutHelper(ProviderCreator<T> creator,
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mProvider = creator.createProvider(this, new SuperProvider());
+    }
+
+    /** @hide */
+    // TODO @SystemApi
+    public T getProvider() {
+        return mProvider;
+    }
+
+    @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
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    /** @hide */
+    public class SuperProvider implements ViewProvider {
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return FrameLayoutHelper.super.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return FrameLayoutHelper.super.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return FrameLayoutHelper.super.onTrackballEvent(ev);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            FrameLayoutHelper.super.onFinishInflate();
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            FrameLayoutHelper.super.setEnabled(enabled);
+        }
+
+        @Override
+        public void onAttachedToWindow_impl() {
+            FrameLayoutHelper.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            FrameLayoutHelper.super.onDetachedFromWindow();
+        }
+    }
+
+    /** @hide */
+    @FunctionalInterface
+    public interface ProviderCreator<U extends ViewProvider> {
+        U createProvider(FrameLayoutHelper<U> instance, ViewProvider superProvider);
+    }
+}
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 4464f8f..95fe363 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -36,9 +36,10 @@
 // TODO @SystemApi
 public interface MediaControlView2Provider extends ViewProvider {
     void setController_impl(MediaController controller);
-    void show_impl();
-    void show_impl(long timeout);
     boolean isShowing_impl();
-    void hide_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..87f509a 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
 
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 8ff0ea5..da4d0c7 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -16,15 +16,18 @@
 
 package android.media.update;
 
-import android.media.AudioAttributes;
+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.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;
@@ -36,9 +39,8 @@
 /**
  * @hide
  */
+// TODO: @SystemApi
 public interface MediaSession2Provider extends TransportControlProvider {
-    void initialize();
-
     void close_impl();
     void setPlayer_impl(MediaPlayerInterface player);
     void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider);
@@ -61,6 +63,25 @@
     void addPlaybackListener_impl(Executor executor, PlaybackListener listener);
     void removePlaybackListener_impl(PlaybackListener listener);
 
+    interface CommandProvider {
+        int getCommandCode_impl();
+        String getCustomCommand_impl();
+        Bundle getExtra_impl();
+        Bundle toBundle_impl();
+
+        boolean equals_impl(Object ob);
+        int hashCode_impl();
+    }
+
+    interface CommandGroupProvider {
+        void addCommand_impl(Command command);
+        void addAllPredefinedCommands_impl();
+        void removeCommand_impl(Command command);
+        boolean hasCommand_impl(Command command);
+        boolean hasCommand_impl(int code);
+        Bundle toBundle_impl();
+    }
+
     interface ControllerInfoProvider {
         String getPackageName_impl();
         int getUid_impl();
@@ -68,4 +89,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/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/media/java/android/media/update/PlaybackInfoProvider.java
similarity index 60%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
copy to media/java/android/media/update/PlaybackInfoProvider.java
index 01cca2db..36eb58a 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
+++ b/media/java/android/media/update/PlaybackInfoProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 The Android Open Source Project
+ * 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.
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.media.update;
 
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.media.AudioAttributes;
 
 /**
- * See ImsService#Listener for more information.
- * {@hide}
+ * @hide
  */
-oneway interface IImsServiceControllerListener {
-    void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c);
+// 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/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/media/java/android/media/update/ProviderCreator.java
similarity index 74%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to media/java/android/media/update/ProviderCreator.java
index d648a35..f5f3e47 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/media/java/android/media/update/ProviderCreator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.media.update;
 
-parcelable ImsStreamMediaProfile;
+/** @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 14b23c8..922b452 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,7 +17,6 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.media.DataSourceDesc;
 import android.media.MediaBrowser2;
@@ -27,17 +26,24 @@
 import android.media.MediaItem2;
 import android.media.MediaLibraryService2;
 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.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
+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.MediaSession2Provider.BuilderBaseProvider;
+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.os.Bundle;
 import android.os.IInterface;
 import android.util.AttributeSet;
@@ -60,29 +66,64 @@
             VideoView2 instance, ViewProvider superProvider,
             @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);
-    ControllerInfoProvider createMediaSession2ControllerInfoProvider(Context context,
+    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 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);
+    BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
+            Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
+
     MediaController2Provider createMediaController2(Context context, MediaController2 instance,
             SessionToken2 token, Executor executor, ControllerCallback callback);
+
     MediaBrowser2Provider createMediaBrowser2(Context context, MediaBrowser2 instance,
             SessionToken2 token, Executor executor, BrowserCallback callback);
+
     MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
+
     MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
-    MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(Context context,
-            MediaLibrarySession instance, MediaPlayerInterface player, String id,
-            VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
-            Executor executor, MediaLibrarySessionCallback callback);
+    BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
+        createMediaLibraryService2Builder(
+            Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
+            Executor callbackExecutor, MediaLibrarySessionCallback callback);
+
     SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid);
     SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle);
 
     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 c5b58f7..10f03d2 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -47,8 +47,7 @@
     void setMediaControlView2_impl(MediaControlView2 mediaControlView);
     MediaController getMediaController_impl();
     MediaControlView2 getMediaControlView2_impl();
-    void showSubtitle_impl();
-    void hideSubtitle_impl();
+    void showSubtitle_impl(boolean show);
     // TODO: remove setSpeed_impl once MediaController2 is ready.
     void setSpeed_impl(float speed);
     void setAudioFocusRequest_impl(int focusGain);
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/media/java/android/media/update/VolumeProvider2Provider.java
similarity index 64%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
copy to media/java/android/media/update/VolumeProvider2Provider.java
index f6005b6..5657af6 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
+++ b/media/java/android/media/update/VolumeProvider2Provider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 The Android Open Source Project
+ * 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.
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.telephony.ims.internal.aidl;
+package android.media.update;
 
 /**
- * See RcsFeature for more information.
- * {@hide}
+ * @hide
  */
-interface IImsRcsFeature {
-    //Empty Default Implementation
-}
\ No newline at end of file
+// 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_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 95b07f1..4f06caa 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1134,6 +1134,27 @@
     return ListOfVectorsToArrayListOfByteArray(env, secureStops);
 }
 
+static jobject android_media_MediaDrm_getSecureStopIds(
+    JNIEnv *env, jobject thiz) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (drm == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "MediaDrm obj is null");
+        return NULL;
+    }
+
+    List<Vector<uint8_t> > secureStopIds;
+
+    status_t err = drm->getSecureStopIds(secureStopIds);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get secure stop Ids")) {
+        return NULL;
+    }
+
+    return ListOfVectorsToArrayListOfByteArray(env, secureStopIds);
+}
+
 static jbyteArray android_media_MediaDrm_getSecureStop(
     JNIEnv *env, jobject thiz, jbyteArray ssid) {
     sp<IDrm> drm = GetDrm(env, thiz);
@@ -1168,7 +1189,22 @@
     throwExceptionAsNecessary(env, err, "Failed to release secure stops");
 }
 
-static void android_media_MediaDrm_releaseAllSecureStops(
+static void android_media_MediaDrm_removeSecureStop(
+        JNIEnv *env, jobject thiz, jbyteArray ssid) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (drm == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "MediaDrm obj is null");
+        return;
+    }
+
+    status_t err = drm->removeSecureStop(JByteArrayToVector(env, ssid));
+
+    throwExceptionAsNecessary(env, err, "Failed to remove secure stop");
+}
+
+static void android_media_MediaDrm_removeAllSecureStops(
     JNIEnv *env, jobject thiz) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
@@ -1176,9 +1212,9 @@
         return;
     }
 
-    status_t err = drm->releaseAllSecureStops();
+    status_t err = drm->removeAllSecureStops();
 
-    throwExceptionAsNecessary(env, err, "Failed to release all secure stops");
+    throwExceptionAsNecessary(env, err, "Failed to remove all secure stops");
 }
 
 
@@ -1719,14 +1755,20 @@
     { "getSecureStops", "()Ljava/util/List;",
       (void *)android_media_MediaDrm_getSecureStops },
 
+    { "getSecureStopIds", "()Ljava/util/List;",
+      (void *)android_media_MediaDrm_getSecureStopIds },
+
     { "getSecureStop", "([B)[B",
       (void *)android_media_MediaDrm_getSecureStop },
 
     { "releaseSecureStops", "([B)V",
       (void *)android_media_MediaDrm_releaseSecureStops },
 
-    { "releaseAllSecureStops", "()V",
-      (void *)android_media_MediaDrm_releaseAllSecureStops },
+    { "removeSecureStop", "([B)V",
+      (void *)android_media_MediaDrm_removeSecureStop },
+
+    { "removeAllSecureStops", "()V",
+      (void *)android_media_MediaDrm_removeAllSecureStops },
 
     { "getConnectedHdcpLevel", "()I",
       (void *)android_media_MediaDrm_getConnectedHdcpLevel },
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
index ea1ac2b..e88c0f1 100644
--- a/media/lib/remotedisplay/Android.mk
+++ b/media/lib/remotedisplay/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.media.remotedisplay
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index b0d3177..69ca4d2 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.mediadrm.signer
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/tvremote/Android.mk b/media/lib/tvremote/Android.mk
index 06838c2..1ffdd62 100644
--- a/media/lib/tvremote/Android.mk
+++ b/media/lib/tvremote/Android.mk
@@ -22,9 +22,7 @@
 LOCAL_MODULE:= com.android.media.tv.remoteprovider
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java) \
-            $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
 LOCAL_DEX_PREOPT := false
 
@@ -45,4 +43,4 @@
 
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 
-include $(BUILD_PREBUILT)
\ No newline at end of file
+include $(BUILD_PREBUILT)
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/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 69287e8..4338d26 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -14,10 +14,10 @@
     android-support-v7-preference \
     android-support-v7-appcompat \
     android-support-v14-preference \
-    apptoolkit-lifecycle-runtime
+    android-arch-lifecycle-runtime
 
 LOCAL_SHARED_JAVA_LIBRARIES := \
-    apptoolkit-lifecycle-common
+    android-arch-lifecycle-common
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 14f0625..3c2ca2d 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -64,7 +64,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES += \
     android-support-annotations \
     android-support-v4 \
-    apptoolkit-lifecycle-runtime \
-    apptoolkit-lifecycle-common \
+    android-arch-lifecycle-runtime \
+    android-arch-lifecycle-common \
     SettingsLib
 endif
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 251ae2d..2bcf4ef 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -48,8 +48,8 @@
     android-slices-core \
     android-slices-view \
     android-slices-builders \
-    apptoolkit-arch-core-runtime \
-    apptoolkit-lifecycle-extensions \
+    android-arch-core-runtime \
+    android-arch-lifecycle-extensions \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-tags \
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 18d27bb..53f7e44 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -26,14 +26,26 @@
 @DependsOn(target = QSIconView.class)
 @DependsOn(target = QSTile.class)
 public abstract class QSTileView extends LinearLayout {
-    public static final int VERSION = 1;
+    public static final int VERSION = 2;
 
     public QSTileView(Context context) {
         super(context);
     }
 
     public abstract View updateAccessibilityOrder(View previousView);
+
+    /**
+     * Returns a {@link QSIconView} containing only the icon for this tile. Use
+     * {@link #getIconWithBackground()} to retrieve the entire tile (background & peripherals
+     * included).
+     */
     public abstract QSIconView getIcon();
+
+    /**
+     * Returns a {@link View} containing the icon for this tile along with the accompanying
+     * background circle/peripherals. To retrieve only the inner icon, use {@link #getIcon()}.
+     */
+    public abstract View getIconWithBackground();
     public abstract void init(QSTile tile);
     public abstract void onStateChanged(State state);
 
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..997fe6d 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -32,80 +32,108 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="16dp"
+        android:layout_height="match_parent"
+        android:layout_marginStart="8dp"
         android:layout_marginEnd="8dp"
-        android:gravity="end">
+        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/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 1d1f95b..3e80085 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -61,6 +61,7 @@
                 android:layout_width="24dp"
                 android:layout_height="24dp"
                 android:background="?android:selectableItemBackgroundBorderless"
+                android:contentDescription="@string/accessibility_output_chooser"
                 style="@style/VolumeButtons"
                 android:layout_centerVertical="true"
                 android:src="@drawable/ic_swap"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6768470..1cc1cc8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -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..72615ce 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1272,6 +1272,9 @@
     <!-- 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. -->
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/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..653e500 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -707,10 +707,6 @@
                     && !mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser()),
                     mSecondaryDisplayShowing, true /* forceCallbacks */);
-        } else {
-            // The system's keyguard is disabled or missing.
-            setShowingLocked(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()),
-                    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/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 95185c0..001b409 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -44,7 +44,11 @@
     public static final float EXPANDED_TILE_DELAY = .86f;
 
     private final ArrayList<View> mAllViews = new ArrayList<>();
-    private final ArrayList<View> mTopFiveQs = new ArrayList<>();
+    /**
+     * List of {@link View}s representing Quick Settings that are being animated from the quick QS
+     * position to the normal QS panel.
+     */
+    private final ArrayList<View> mQuickQsViews = new ArrayList<>();
     private final QuickQSPanel mQuickQsPanel;
     private final QSPanel mQsPanel;
     private final QS mQs;
@@ -157,7 +161,7 @@
 
         clearAnimationState();
         mAllViews.clear();
-        mTopFiveQs.clear();
+        mQuickQsViews.clear();
 
         QSTileLayout tileLayout = mQsPanel.getTileLayout();
         mAllViews.add((View) tileLayout);
@@ -198,7 +202,7 @@
                 translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
                 translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
 
-                mTopFiveQs.add(tileView.getIcon());
+                mQuickQsViews.add(tileView.getIconWithBackground());
                 mAllViews.add(tileView.getIcon());
                 mAllViews.add(quickTileView);
             } else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -322,9 +326,9 @@
     @Override
     public void onAnimationAtEnd() {
         mQuickQsPanel.setVisibility(View.INVISIBLE);
-        final int N = mTopFiveQs.size();
+        final int N = mQuickQsViews.size();
         for (int i = 0; i < N; i++) {
-            mTopFiveQs.get(i).setVisibility(View.VISIBLE);
+            mQuickQsViews.get(i).setVisibility(View.VISIBLE);
         }
     }
 
@@ -332,9 +336,9 @@
     public void onAnimationStarted() {
         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnFirstPage) {
-            final int N = mTopFiveQs.size();
+            final int N = mQuickQsViews.size();
             for (int i = 0; i < N; i++) {
-                mTopFiveQs.get(i).setVisibility(View.INVISIBLE);
+                mQuickQsViews.get(i).setVisibility(View.INVISIBLE);
             }
         }
     }
@@ -348,9 +352,9 @@
             v.setTranslationX(0);
             v.setTranslationY(0);
         }
-        final int N2 = mTopFiveQs.size();
+        final int N2 = mQuickQsViews.size();
         for (int i = 0; i < N2; i++) {
-            mTopFiveQs.get(i).setVisibility(View.VISIBLE);
+            mQuickQsViews.get(i).setVisibility(View.VISIBLE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 92475da..76baee4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -84,6 +84,8 @@
     protected View mEdit;
     private TouchAnimator mAnimator;
 
+    private View mActionsContainer;
+
     public QSFooterImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -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,8 @@
     @Nullable
     private TouchAnimator createSettingsAlphaAnimator() {
         return new TouchAnimator.Builder()
-                .addFloat(mEdit, "alpha", 0, 1)
-                .addFloat(mMultiUserSwitch, "alpha", 0, 1)
                 .addFloat(mCarrierText, "alpha", 0, 1)
-                .addFloat(mSettingsButton, "alpha", 0, 1)
+                .addFloat(mActionsContainer, "alpha", 0, 1)
                 .build();
     }
 
@@ -269,6 +271,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/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index b4cfda6..c9c678c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -236,6 +236,10 @@
         return mIcon;
     }
 
+    public View getIconWithBackground() {
+        return mIconFrame;
+    }
+
     @Override
     public boolean performClick() {
         mClicked = true;
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/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/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3ebeb4d..3dfb913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by removing all heads-
         // up notifications.
-        mHeadsUpManager.removeAllHeadsUpEntries();
+        mHeadsUpManager.releaseAllImmediately();
         super.animateExpandNotificationsPanel();
     }
 
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/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
new file mode 100644
index 0000000..c45c538
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -0,0 +1,455 @@
+/*
+ * 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.statusbar.phone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.v4.util.ArraySet;
+import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Stack;
+
+/**
+ * A implementation of HeadsUpManager for phone and car.
+ */
+public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
+       ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
+       OnHeadsUpChangedListener {
+    private static final String TAG = "HeadsUpManagerPhone";
+    private static final boolean DEBUG = false;
+
+    private final View mStatusBarWindowView;
+    private final int mStatusBarHeight;
+    private final NotificationGroupManager mGroupManager;
+    private final StatusBar mBar;
+    private final VisualStabilityManager mVisualStabilityManager;
+
+    private boolean mReleaseOnExpandFinish;
+    private boolean mTrackingHeadsUp;
+    private HashSet<String> mSwipedOutKeys = new HashSet<>();
+    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+            = new ArraySet<>();
+    private boolean mIsExpanded;
+    private int[] mTmpTwoArray = new int[2];
+    private boolean mHeadsUpGoingAway;
+    private boolean mWaitingOnCollapseWhenGoingAway;
+    private boolean mIsObserving;
+    private int mStatusBarState;
+
+    private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
+        private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
+
+        @Override
+        public HeadsUpEntryPhone acquire() {
+            if (!mPoolObjects.isEmpty()) {
+                return mPoolObjects.pop();
+            }
+            return new HeadsUpEntryPhone();
+        }
+
+        @Override
+        public boolean release(@NonNull HeadsUpEntryPhone instance) {
+            instance.reset();
+            mPoolObjects.push(instance);
+            return true;
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  Constructor:
+
+    public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
+            @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
+            @NonNull VisualStabilityManager visualStabilityManager) {
+        super(context);
+
+        mStatusBarWindowView = statusBarWindowView;
+        mGroupManager = groupManager;
+        mBar = bar;
+        mVisualStabilityManager = visualStabilityManager;
+
+        Resources resources = mContext.getResources();
+        mStatusBarHeight = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+
+        addListener(new OnHeadsUpChangedListener() {
+            @Override
+            public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
+                if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
+                updateTouchableRegionListener();
+            }
+        });
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  Public methods:
+
+    /**
+     * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+     * that a user might have consciously clicked on it.
+     *
+     * @param key the key of the touched notification
+     * @return whether the touch is invalid and should be discarded
+     */
+    public boolean shouldSwallowClick(@NonNull String key) {
+        HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
+            return true;
+        }
+        return false;
+    }
+
+    public void onExpandingFinished() {
+        if (mReleaseOnExpandFinish) {
+            releaseAllImmediately();
+            mReleaseOnExpandFinish = false;
+        } else {
+            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+                if (isHeadsUp(entry.key)) {
+                    // Maybe the heads-up was removed already
+                    removeHeadsUpEntry(entry);
+                }
+            }
+        }
+        mEntriesToRemoveAfterExpand.clear();
+    }
+
+    /**
+     * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
+     * from the list even after a Heads Up Notification is gone.
+     */
+    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+        mTrackingHeadsUp = trackingHeadsUp;
+    }
+
+    /**
+     * Notify that the status bar panel gets expanded or collapsed.
+     *
+     * @param isExpanded True to notify expanded, false to notify collapsed.
+     */
+    public void setIsPanelExpanded(boolean isExpanded) {
+        if (isExpanded != mIsExpanded) {
+            mIsExpanded = isExpanded;
+            if (isExpanded) {
+                // make sure our state is sane
+                mWaitingOnCollapseWhenGoingAway = false;
+                mHeadsUpGoingAway = false;
+                updateTouchableRegionListener();
+            }
+        }
+    }
+
+    /**
+     * Set the current state of the statusbar.
+     */
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+    }
+
+    /**
+     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+     * animating out. This is used to keep the touchable regions in a sane state.
+     */
+    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+        if (headsUpGoingAway != mHeadsUpGoingAway) {
+            mHeadsUpGoingAway = headsUpGoingAway;
+            if (!headsUpGoingAway) {
+                waitForStatusBarLayout();
+            }
+            updateTouchableRegionListener();
+        }
+    }
+
+    /**
+     * Notifies that a remote input textbox in notification gets active or inactive.
+     * @param entry The entry of the target notification.
+     * @param remoteInputActive True to notify active, False to notify inactive.
+     */
+    public void setRemoteInputActive(
+            @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+        HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
+        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+            headsUpEntry.remoteInputActive = remoteInputActive;
+            if (remoteInputActive) {
+                headsUpEntry.removeAutoRemovalCallbacks();
+            } else {
+                headsUpEntry.updateEntry(false /* updatePostTime */);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public void removeMinimumDisplayTimeForTesting() {
+        mMinimumDisplayTime = 1;
+        mHeadsUpNotificationDecay = 1;
+        mTouchAcceptanceDelay = 1;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  HeadsUpManager public methods overrides:
+
+    @Override
+    public boolean isTrackingHeadsUp() {
+        return mTrackingHeadsUp;
+    }
+
+    @Override
+    public void snooze() {
+        super.snooze();
+        mReleaseOnExpandFinish = true;
+    }
+
+    /**
+     * React to the removal of the notification in the heads up.
+     *
+     * @return true if the notification was removed and false if it still needs to be kept around
+     * for a bit since it wasn't shown long enough
+     */
+    @Override
+    public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+        if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+            return super.removeNotification(key, ignoreEarliestRemovalTime);
+        } else {
+            HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
+            entry.removeAsSoonAsPossible();
+            return false;
+        }
+    }
+
+    public void addSwipedOutNotification(@NonNull String key) {
+        mSwipedOutKeys.add(key);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  Dumpable overrides:
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("HeadsUpManagerPhone state:");
+        dumpInternal(fd, pw, args);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  ViewTreeObserver.OnComputeInternalInsetsListener overrides:
+
+    /**
+     * Overridden from TreeObserver.
+     */
+    @Override
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+        if (mIsExpanded || mBar.isBouncerShowing()) {
+            // The touchable region is always the full area when expanded
+            return;
+        }
+        if (hasPinnedHeadsUp()) {
+            ExpandableNotificationRow topEntry = getTopEntry().row;
+            if (topEntry.isChildInGroup()) {
+                final ExpandableNotificationRow groupSummary
+                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+                if (groupSummary != null) {
+                    topEntry = groupSummary;
+                }
+            }
+            topEntry.getLocationOnScreen(mTmpTwoArray);
+            int minX = mTmpTwoArray[0];
+            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+            int maxY = topEntry.getIntrinsicHeight();
+
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(minX, 0, maxX, maxY);
+        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  VisualStabilityManager.Callback overrides:
+
+    @Override
+    public void onReorderingAllowed() {
+        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+            if (isHeadsUp(entry.key)) {
+                // Maybe the heads-up was removed already
+                removeHeadsUpEntry(entry);
+            }
+        }
+        mEntriesToRemoveWhenReorderingAllowed.clear();
+        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  HeadsUpManager utility (protected) methods overrides:
+
+    @Override
+    protected HeadsUpEntry createHeadsUpEntry() {
+        return mEntryPool.acquire();
+    }
+
+    @Override
+    protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
+        mEntryPool.release((HeadsUpEntryPhone) entry);
+    }
+
+    @Override
+    protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+          return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
+                  || super.shouldHeadsUpBecomePinned(entry);
+    }
+
+    @Override
+    protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dumpInternal(fd, pw, args);
+        pw.print("  mStatusBarState="); pw.println(mStatusBarState);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  Protected utility methods:
+
+    @Nullable
+    protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
+        return (HeadsUpEntryPhone) getHeadsUpEntry(key);
+    }
+
+    @Nullable
+    protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
+        return (HeadsUpEntryPhone) getTopHeadsUpEntry();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  Private utility methods:
+
+    private boolean wasShownLongEnough(@NonNull String key) {
+        if (mSwipedOutKeys.contains(key)) {
+            // We always instantly dismiss views being manually swiped out.
+            mSwipedOutKeys.remove(key);
+            return true;
+        }
+
+        HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
+        HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
+        if (headsUpEntry != topEntry) {
+            return true;
+        }
+        return headsUpEntry.wasShownLongEnough();
+    }
+
+    /**
+     * We need to wait on the whole panel to collapse, before we can remove the touchable region
+     * listener.
+     */
+    private void waitForStatusBarLayout() {
+        mWaitingOnCollapseWhenGoingAway = true;
+        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
+                    mWaitingOnCollapseWhenGoingAway = false;
+                    updateTouchableRegionListener();
+                }
+            }
+        });
+    }
+
+    private void updateTouchableRegionListener() {
+        boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
+                || mWaitingOnCollapseWhenGoingAway;
+        if (shouldObserve == mIsObserving) {
+            return;
+        }
+        if (shouldObserve) {
+            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+            mStatusBarWindowView.requestLayout();
+        } else {
+            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        }
+        mIsObserving = shouldObserve;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //  HeadsUpEntryPhone:
+
+    protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+        public void setEntry(@NonNull final NotificationData.Entry entry) {
+           Runnable removeHeadsUpRunnable = () -> {
+                if (!mVisualStabilityManager.isReorderingAllowed()) {
+                    mEntriesToRemoveWhenReorderingAllowed.add(entry);
+                    mVisualStabilityManager.addReorderingAllowedCallback(
+                            HeadsUpManagerPhone.this);
+                } else if (!mTrackingHeadsUp) {
+                    removeHeadsUpEntry(entry);
+                } else {
+                    mEntriesToRemoveAfterExpand.add(entry);
+                }
+            };
+
+            super.setEntry(entry, removeHeadsUpRunnable);
+        }
+
+        public boolean wasShownLongEnough() {
+            return earliestRemovaltime < mClock.currentTimeMillis();
+        }
+
+        @Override
+        public void updateEntry(boolean updatePostTime) {
+            super.updateEntry(updatePostTime);
+
+            if (mEntriesToRemoveAfterExpand.contains(entry)) {
+                mEntriesToRemoveAfterExpand.remove(entry);
+            }
+            if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+                mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+            }
+        }
+
+        @Override
+        public void expanded(boolean expanded) {
+            if (this.expanded == expanded) {
+                return;
+            }
+
+            this.expanded = expanded;
+            if (expanded) {
+                removeAutoRemovalCallbacks();
+            } else {
+                updateEntry(false /* updatePostTime */);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c85571c..2bfdefe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
@@ -31,7 +31,7 @@
  */
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
-    private HeadsUpManager mHeadsUpManager;
+    private HeadsUpManagerPhone mHeadsUpManager;
     private NotificationStackScrollLayout mStackScroller;
     private int mTrackingPointer;
     private float mTouchSlop;
@@ -43,7 +43,7 @@
     private NotificationPanelView mPanel;
     private ExpandableNotificationRow mPickedChild;
 
-    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
+    public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
             NotificationStackScrollLayout stackScroller,
             NotificationPanelView notificationPanelView) {
         mHeadsUpManager = headsUpManager;
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/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 91cae0a..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..52d005c 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,14 +68,11 @@
 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;
 
 public class NotificationPanelView extends PanelView implements
@@ -482,7 +480,7 @@
             mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX);
+        mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
         mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
         requestScrollerTopPaddingUpdate(animate);
     }
@@ -1571,7 +1569,7 @@
     private void updatePanelExpanded() {
         boolean isExpanded = !isFullyCollapsed();
         if (mPanelExpanded != isExpanded) {
-            mHeadsUpManager.setIsExpanded(isExpanded);
+            mHeadsUpManager.setIsPanelExpanded(isExpanded);
             mStatusBar.setPanelExpanded(isExpanded);
             mPanelExpanded = isExpanded;
         }
@@ -2338,7 +2336,7 @@
     }
 
     @Override
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
                 this);
@@ -2630,8 +2628,8 @@
         }
     }
 
-    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
-        mKeyguardStatusView.setPulsing(pulsing != null);
+    public void setPulsing(boolean pulsing) {
+        mKeyguardStatusView.setPulsing(pulsing);
         positionClockAndNotifications();
         mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
                 + mKeyguardStatusView.getClockBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 2b7e474..6daabed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -75,7 +75,7 @@
     }
 
     protected StatusBar mStatusBar;
-    protected HeadsUpManager mHeadsUpManager;
+    protected HeadsUpManagerPhone mHeadsUpManager;
 
     private float mPeekHeight;
     private float mHintDistance;
@@ -1252,7 +1252,7 @@
      */
     protected abstract int getClearAllHeight();
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 20b5018..6444cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -407,17 +407,17 @@
         int iconId = R.drawable.stat_sys_data_bluetooth;
         String contentDescription =
                 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
-        boolean bluetoothEnabled = false;
+        boolean bluetoothVisible = false;
         if (mBluetooth != null) {
-            bluetoothEnabled = mBluetooth.isBluetoothEnabled();
             if (mBluetooth.isBluetoothConnected()) {
                 iconId = R.drawable.stat_sys_data_bluetooth_connected;
                 contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected);
+                bluetoothVisible = mBluetooth.isBluetoothEnabled();
             }
         }
 
         mIconController.setIcon(mSlotBluetooth, iconId, contentDescription);
-        mIconController.setIconVisibility(mSlotBluetooth, bluetoothEnabled);
+        mIconController.setIconVisibility(mSlotBluetooth, bluetoothVisible);
     }
 
     private final void updateTTY() {
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..116e3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,6 +208,7 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -219,6 +220,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -802,15 +804,14 @@
                 .commit();
         mIconController = Dependency.get(StatusBarIconController.class);
 
-        mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
-        mHeadsUpManager.setBar(this);
+        mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
+                mVisualStabilityManager);
         mHeadsUpManager.addListener(this);
         mHeadsUpManager.addListener(mNotificationPanel);
         mHeadsUpManager.addListener(mGroupManager);
         mHeadsUpManager.addListener(mVisualStabilityManager);
         mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
-        mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
         mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -1341,7 +1342,8 @@
 
     @Override
     public void onPerformRemoveNotification(StatusBarNotification n) {
-        if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
+        if (mStackScroller.hasPulsingNotifications() &&
+                    !mHeadsUpManager.hasHeadsUpNotifications()) {
             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
             // Finish the pulse.
             mDozeScrimController.pulseOutNow();
@@ -2090,9 +2092,8 @@
     }
 
     public void maybeEscalateHeadsUp() {
-        Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
-        for (HeadsUpManager.HeadsUpEntry entry : entries) {
-            final StatusBarNotification sbn = entry.entry.notification;
+        mHeadsUpManager.getAllEntries().forEach(entry -> {
+            final StatusBarNotification sbn = entry.notification;
             final Notification notification = sbn.getNotification();
             if (notification.fullScreenIntent != null) {
                 if (DEBUG) {
@@ -2102,11 +2103,11 @@
                     EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
                             sbn.getKey());
                     notification.fullScreenIntent.send();
-                    entry.entry.notifyFullScreenIntentLaunched();
+                    entry.notifyFullScreenIntentLaunched();
                 } catch (PendingIntent.CanceledException e) {
                 }
             }
-        }
+        });
         mHeadsUpManager.releaseAllImmediately();
     }
 
@@ -4636,24 +4637,22 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
-                            mHeadsUpManager.getAllEntries();
-                    if (!pulsingEntries.isEmpty()) {
+                    if (mHeadsUpManager.hasHeadsUpNotifications()) {
                         // Only pulse the stack scroller if there's actually something to show.
                         // Otherwise just show the always-on screen.
-                        setPulsing(pulsingEntries);
+                        setPulsing(true);
                     }
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
-                    setPulsing(null);
+                    setPulsing(false);
                 }
 
-                private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+                private void setPulsing(boolean pulsing) {
                     mNotificationPanel.setPulsing(pulsing);
-                    mVisualStabilityManager.setPulsing(pulsing != null);
+                    mVisualStabilityManager.setPulsing(pulsing);
                     mIgnoreTouchWhilePulsing = false;
                 }
             }, reason);
@@ -4801,7 +4800,7 @@
 
 
     // for heads up notifications
-    protected HeadsUpManager mHeadsUpManager;
+    protected HeadsUpManagerPhone mHeadsUpManager;
 
     private AboveShelfObserver mAboveShelfObserver;
 
@@ -4904,7 +4903,7 @@
                 // Release the HUN notification to the shade.
 
                 if (isPresenterFullyCollapsed()) {
-                    HeadsUpManager.setIsClickedNotification(row, true);
+                    HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
                 }
                 //
                 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
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/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 53dfb24..a2b896d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,118 +16,68 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.os.SystemClock;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.support.v4.util.ArraySet;
 import android.util.ArrayMap;
+import android.provider.Settings;
 import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Iterator;
+import java.util.stream.Stream;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Stack;
 
 /**
  * A manager which handles heads up notifications which is a special mode where
  * they simply peek from the top of the screen.
  */
-public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
-        VisualStabilityManager.Callback {
+public class HeadsUpManager {
     private static final String TAG = "HeadsUpManager";
     private static final boolean DEBUG = false;
     private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
-    private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
 
-    private final int mHeadsUpNotificationDecay;
-    private final int mMinimumDisplayTime;
+    protected final Clock mClock = new Clock();
+    protected final Context mContext;
+    protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+    protected final Handler mHandler = new Handler(Looper.getMainLooper());
 
-    private final int mTouchAcceptanceDelay;
+    protected int mHeadsUpNotificationDecay;
+    protected int mMinimumDisplayTime;
+    protected int mTouchAcceptanceDelay;
+    protected int mSnoozeLengthMs;
+    protected boolean mHasPinnedNotification;
+    protected int mUser;
+
+    private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
     private final ArrayMap<String, Long> mSnoozedPackages;
-    private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
-    private final int mDefaultSnoozeLengthMs;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
+    private final ContentObserver mSettingsObserver;
 
-        private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
-
-        @Override
-        public HeadsUpEntry acquire() {
-            if (!mPoolObjects.isEmpty()) {
-                return mPoolObjects.pop();
-            }
-            return new HeadsUpEntry();
-        }
-
-        @Override
-        public boolean release(HeadsUpEntry instance) {
-            instance.reset();
-            mPoolObjects.push(instance);
-            return true;
-        }
-    };
-
-    private final View mStatusBarWindowView;
-    private final int mStatusBarHeight;
-    private final Context mContext;
-    private final NotificationGroupManager mGroupManager;
-    private StatusBar mBar;
-    private int mSnoozeLengthMs;
-    private ContentObserver mSettingsObserver;
-    private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
-    private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private int mUser;
-    private Clock mClock;
-    private boolean mReleaseOnExpandFinish;
-    private boolean mTrackingHeadsUp;
-    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
-            = new ArraySet<>();
-    private boolean mIsExpanded;
-    private boolean mHasPinnedNotification;
-    private int[] mTmpTwoArray = new int[2];
-    private boolean mHeadsUpGoingAway;
-    private boolean mWaitingOnCollapseWhenGoingAway;
-    private boolean mIsObserving;
-    private boolean mRemoteInputActive;
-    private float mExpandedHeight;
-    private VisualStabilityManager mVisualStabilityManager;
-    private int mStatusBarState;
-
-    public HeadsUpManager(final Context context, View statusBarWindowView,
-                          NotificationGroupManager groupManager) {
+    public HeadsUpManager(@NonNull final Context context) {
         mContext = context;
-        Resources resources = mContext.getResources();
-        mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
-        mSnoozedPackages = new ArrayMap<>();
-        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
-        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+        Resources resources = context.getResources();
         mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
         mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
-        mClock = new Clock();
+        mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
+        mSnoozedPackages = new ArrayMap<>();
+        int defaultSnoozeLengthMs =
+                resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
 
         mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
-                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
+                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
         mSettingsObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
@@ -142,47 +92,26 @@
         context.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
                 mSettingsObserver);
-        mStatusBarWindowView = statusBarWindowView;
-        mGroupManager = groupManager;
-        mStatusBarHeight = resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
     }
 
-    private void updateTouchableRegionListener() {
-        boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
-                || mWaitingOnCollapseWhenGoingAway;
-        if (shouldObserve == mIsObserving) {
-            return;
-        }
-        if (shouldObserve) {
-            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-            mStatusBarWindowView.requestLayout();
-        } else {
-            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
-        }
-        mIsObserving = shouldObserve;
-    }
-
-    public void setBar(StatusBar bar) {
-        mBar = bar;
-    }
-
-    public void addListener(OnHeadsUpChangedListener listener) {
+    /**
+     * Adds an OnHeadUpChangedListener to observe events.
+     */
+    public void addListener(@NonNull OnHeadsUpChangedListener listener) {
         mListeners.add(listener);
     }
 
-    public void removeListener(OnHeadsUpChangedListener listener) {
+    /**
+     * Removes the OnHeadUpChangedListener from the observer list.
+     */
+    public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
         mListeners.remove(listener);
     }
 
-    public StatusBar getBar() {
-        return mBar;
-    }
-
     /**
      * Called when posting a new notification to the heads up.
      */
-    public void showNotification(NotificationData.Entry headsUp) {
+    public void showNotification(@NonNull NotificationData.Entry headsUp) {
         if (DEBUG) Log.v(TAG, "showNotification");
         addHeadsUpEntry(headsUp);
         updateNotification(headsUp, true);
@@ -192,7 +121,7 @@
     /**
      * Called when updating or posting a notification to the heads up.
      */
-    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
+    public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
         if (DEBUG) Log.v(TAG, "updateNotification");
 
         headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -204,14 +133,13 @@
                 // with the groupmanager
                 return;
             }
-            headsUpEntry.updateEntry();
+            headsUpEntry.updateEntry(true /* updatePostTime */);
             setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
         }
     }
 
-    private void addHeadsUpEntry(NotificationData.Entry entry) {
-        HeadsUpEntry headsUpEntry = mEntryPool.acquire();
-
+    private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
+        HeadsUpEntry headsUpEntry = createHeadsUpEntry();
         // This will also add the entry to the sortedList
         headsUpEntry.setEntry(entry);
         mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -223,16 +151,17 @@
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
-    private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
-        return mStatusBarState != StatusBarState.KEYGUARD
-                && !mIsExpanded || hasFullScreenIntent(entry);
+    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+        return hasFullScreenIntent(entry);
     }
 
-    private boolean hasFullScreenIntent(NotificationData.Entry entry) {
+    protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
         return entry.notification.getNotification().fullScreenIntent != null;
     }
 
-    private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
+    protected void setEntryPinned(
+            @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
+        if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
         ExpandableNotificationRow row = headsUpEntry.entry.row;
         if (row.isPinned() != isPinned) {
             row.setPinned(isPinned);
@@ -247,33 +176,35 @@
         }
     }
 
-    private void removeHeadsUpEntry(NotificationData.Entry entry) {
+    protected void removeHeadsUpEntry(NotificationData.Entry entry) {
         HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
+        onHeadsUpEntryRemoved(remove);
+        releaseHeadsUpEntry(remove);
+    }
+
+    protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) {
+        NotificationData.Entry entry = remove.entry;
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         entry.row.setHeadsUp(false);
         setEntryPinned(remove, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
-        mEntryPool.release(remove);
     }
 
-    public void removeAllHeadsUpEntries() {
-        for (String key : mHeadsUpEntries.keySet()) {
-            removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
-        }
-    }
-
-    private void updatePinnedMode() {
+    protected void updatePinnedMode() {
         boolean hasPinnedNotification = hasPinnedNotificationInternal();
         if (hasPinnedNotification == mHasPinnedNotification) {
             return;
         }
+        if (DEBUG) {
+            Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
+                       hasPinnedNotification);
+        }
         mHasPinnedNotification = hasPinnedNotification;
         if (mHasPinnedNotification) {
             MetricsLogger.count(mContext, "note_peek", 1);
         }
-        updateTouchableRegionListener();
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
         }
@@ -285,47 +216,36 @@
      * @return true if the notification was removed and false if it still needs to be kept around
      * for a bit since it wasn't shown long enough
      */
-    public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
-        if (DEBUG) Log.v(TAG, "remove");
-        if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
-            releaseImmediately(key);
-            return true;
-        } else {
-            getHeadsUpEntry(key).removeAsSoonAsPossible();
-            return false;
-        }
+    public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+        if (DEBUG) Log.v(TAG, "removeNotification");
+        releaseImmediately(key);
+        return true;
     }
 
-    private boolean wasShownLongEnough(String key) {
-        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
-        HeadsUpEntry topEntry = getTopEntry();
-        if (mSwipedOutKeys.contains(key)) {
-            // We always instantly dismiss views being manually swiped out.
-            mSwipedOutKeys.remove(key);
-            return true;
-        }
-        if (headsUpEntry != topEntry) {
-            return true;
-        }
-        return headsUpEntry.wasShownLongEnough();
-    }
-
+    /**
+     * Returns if the given notification is in the Heads Up Notification list or not.
+     */
     public boolean isHeadsUp(String key) {
         return mHeadsUpEntries.containsKey(key);
     }
 
     /**
-     * Push any current Heads Up notification down into the shade.
+     * Pushes any current Heads Up notification down into the shade.
      */
     public void releaseAllImmediately() {
         if (DEBUG) Log.v(TAG, "releaseAllImmediately");
-        ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
-        for (String key : keys) {
-            releaseImmediately(key);
+        Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
+        while (iterator.hasNext()) {
+            HeadsUpEntry entry = iterator.next();
+            iterator.remove();
+            onHeadsUpEntryRemoved(entry);
         }
     }
 
-    public void releaseImmediately(String key) {
+    /**
+     * Pushes the given Heads Up notification down into the shade.
+     */
+    public void releaseImmediately(@NonNull String key) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
         if (headsUpEntry == null) {
             return;
@@ -334,11 +254,14 @@
         removeHeadsUpEntry(shadeEntry);
     }
 
-    public boolean isSnoozed(String packageName) {
+    /**
+     * Returns if the given notification is snoozed or not.
+     */
+    public boolean isSnoozed(@NonNull String packageName) {
         final String key = snoozeKey(packageName, mUser);
         Long snoozedUntil = mSnoozedPackages.get(key);
         if (snoozedUntil != null) {
-            if (snoozedUntil > SystemClock.elapsedRealtime()) {
+            if (snoozedUntil > mClock.currentTimeMillis()) {
                 if (DEBUG) Log.v(TAG, key + " snoozed");
                 return true;
             }
@@ -347,33 +270,61 @@
         return false;
     }
 
+    /**
+     * Snoozes all current Heads Up Notifications.
+     */
     public void snooze() {
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
             String packageName = entry.entry.notification.getPackageName();
             mSnoozedPackages.put(snoozeKey(packageName, mUser),
-                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+                    mClock.currentTimeMillis() + mSnoozeLengthMs);
         }
-        mReleaseOnExpandFinish = true;
     }
 
-    private static String snoozeKey(String packageName, int user) {
+    private static String snoozeKey(@NonNull String packageName, int user) {
         return user + "," + packageName;
     }
 
-    private HeadsUpEntry getHeadsUpEntry(String key) {
+    protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
         return mHeadsUpEntries.get(key);
     }
 
-    public NotificationData.Entry getEntry(String key) {
-        return mHeadsUpEntries.get(key).entry;
+    /**
+     * Returns the entry of given Heads Up Notification.
+     *
+     * @param key Key of heads up notification
+     */
+    public NotificationData.Entry getEntry(@NonNull String key) {
+        HeadsUpEntry entry = mHeadsUpEntries.get(key);
+        return entry != null ? entry.entry : null;
     }
 
-    public Collection<HeadsUpEntry> getAllEntries() {
-        return mHeadsUpEntries.values();
+    /**
+     * Returns the stream of all current Heads Up Notifications.
+     */
+    @NonNull
+    public Stream<NotificationData.Entry> getAllEntries() {
+        return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
     }
 
-    public HeadsUpEntry getTopEntry() {
+    /**
+     * Returns the top Heads Up Notification, which appeares to show at first.
+     */
+    @Nullable
+    public NotificationData.Entry getTopEntry() {
+        HeadsUpEntry topEntry = getTopHeadsUpEntry();
+        return (topEntry != null) ? topEntry.entry : null;
+    }
+
+    /**
+     * Returns if any heads up notification is available or not.
+     */
+    public boolean hasHeadsUpNotifications() {
+        return !mHeadsUpEntries.isEmpty();
+    }
+
+    protected HeadsUpEntry getTopHeadsUpEntry() {
         if (mHeadsUpEntries.isEmpty()) {
             return null;
         }
@@ -387,56 +338,21 @@
     }
 
     /**
-     * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
-     * that a user might have consciously clicked on it.
-     *
-     * @param key the key of the touched notification
-     * @return whether the touch is invalid and should be discarded
+     * Sets the current user.
      */
-    public boolean shouldSwallowClick(String key) {
-        HeadsUpEntry entry = mHeadsUpEntries.get(key);
-        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
-            return true;
-        }
-        return false;
-    }
-
-    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        if (mIsExpanded || mBar.isBouncerShowing()) {
-            // The touchable region is always the full area when expanded
-            return;
-        }
-        if (mHasPinnedNotification) {
-            ExpandableNotificationRow topEntry = getTopEntry().entry.row;
-            if (topEntry.isChildInGroup()) {
-                final ExpandableNotificationRow groupSummary
-                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
-                if (groupSummary != null) {
-                    topEntry = groupSummary;
-                }
-            }
-            topEntry.getLocationOnScreen(mTmpTwoArray);
-            int minX = mTmpTwoArray[0];
-            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int maxY = topEntry.getIntrinsicHeight();
-
-            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, 0, maxX, maxY);
-        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
-            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
-        }
-    }
-
     public void setUser(int user) {
         mUser = user;
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("HeadsUpManager state:");
+        dumpInternal(fd, pw, args);
+    }
+
+    protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("  mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
         pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
-        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
+        pw.print("  now="); pw.println(mClock.currentTimeMillis());
         pw.print("  mUser="); pw.println(mUser);
         for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
             pw.print("  HeadsUpEntry="); pw.println(entry.entry);
@@ -449,6 +365,9 @@
         }
     }
 
+    /**
+     * Returns if there are any pinned Heads Up Notifications or not.
+     */
     public boolean hasPinnedHeadsUp() {
         return mHasPinnedNotification;
     }
@@ -464,14 +383,8 @@
     }
 
     /**
-     * Notifies that a notification was swiped out and will be removed.
-     *
-     * @param key the notification key
+     * Unpins all pinned Heads Up Notifications.
      */
-    public void addSwipedOutNotification(String key) {
-        mSwipedOutKeys.add(key);
-    }
-
     public void unpinAll() {
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -481,60 +394,13 @@
         }
     }
 
-    public void onExpandingFinished() {
-        if (mReleaseOnExpandFinish) {
-            releaseAllImmediately();
-            mReleaseOnExpandFinish = false;
-        } else {
-            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
-                if (isHeadsUp(entry.key)) {
-                    // Maybe the heads-up was removed already
-                    removeHeadsUpEntry(entry);
-                }
-            }
-        }
-        mEntriesToRemoveAfterExpand.clear();
-    }
-
-    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
-        mTrackingHeadsUp = trackingHeadsUp;
-    }
-
-    public boolean isTrackingHeadsUp() {
-        return mTrackingHeadsUp;
-    }
-
-    public void setIsExpanded(boolean isExpanded) {
-        if (isExpanded != mIsExpanded) {
-            mIsExpanded = isExpanded;
-            if (isExpanded) {
-                // make sure our state is sane
-                mWaitingOnCollapseWhenGoingAway = false;
-                mHeadsUpGoingAway = false;
-                updateTouchableRegionListener();
-            }
-        }
-    }
-
     /**
-     * @return the height of the top heads up notification when pinned. This is different from the
-     *         intrinsic height, which also includes whether the notification is system expanded and
-     *         is mainly used when dragging down from a heads up notification.
+     * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
+     * well.
      */
-    public int getTopHeadsUpPinnedHeight() {
-        HeadsUpEntry topEntry = getTopEntry();
-        if (topEntry == null || topEntry.entry == null) {
-            return 0;
-        }
-        ExpandableNotificationRow row = topEntry.entry.row;
-        if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
-                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null) {
-                row = groupSummary;
-            }
-        }
-        return row.getPinnedHeadsUpHeight();
+    public boolean isTrackingHeadsUp() {
+        // Might be implemented in subclass.
+        return false;
     }
 
     /**
@@ -553,147 +419,67 @@
     }
 
     /**
-     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
-     * animating out. This is used to keep the touchable regions in a sane state.
+     * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned
+     * until it's collapsed again.
      */
-    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
-        if (headsUpGoingAway != mHeadsUpGoingAway) {
-            mHeadsUpGoingAway = headsUpGoingAway;
-            if (!headsUpGoingAway) {
-                waitForStatusBarLayout();
-            }
-            updateTouchableRegionListener();
-        }
-    }
-
-    /**
-     * We need to wait on the whole panel to collapse, before we can remove the touchable region
-     * listener.
-     */
-    private void waitForStatusBarLayout() {
-        mWaitingOnCollapseWhenGoingAway = true;
-        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft,
-                    int oldTop, int oldRight, int oldBottom) {
-                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
-                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
-                    mWaitingOnCollapseWhenGoingAway = false;
-                    updateTouchableRegionListener();
-                }
-            }
-        });
-    }
-
-    public static void setIsClickedNotification(View child, boolean clicked) {
-        child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
-    }
-
-    public static boolean isClickedHeadsUpNotification(View child) {
-        Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
-        return clicked != null && clicked;
-    }
-
-    public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
-        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
-        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
-            headsUpEntry.remoteInputActive = remoteInputActive;
-            if (remoteInputActive) {
-                headsUpEntry.removeAutoRemovalCallbacks();
-            } else {
-                headsUpEntry.updateEntry(false /* updatePostTime */);
-            }
-        }
-    }
 
     /**
      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
      * until it's collapsed again.
      */
-    public void setExpanded(NotificationData.Entry entry, boolean expanded) {
-        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
-        if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
-            headsUpEntry.expanded = expanded;
-            if (expanded) {
-                headsUpEntry.removeAutoRemovalCallbacks();
-            } else {
-                headsUpEntry.updateEntry(false /* updatePostTime */);
-            }
+    public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+        HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+        if (headsUpEntry != null && entry.row.isPinned()) {
+            headsUpEntry.expanded(expanded);
         }
     }
 
-    @Override
-    public void onReorderingAllowed() {
-        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
-        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
-            if (isHeadsUp(entry.key)) {
-                // Maybe the heads-up was removed already
-                removeHeadsUpEntry(entry);
-            }
-        }
-        mEntriesToRemoveWhenReorderingAllowed.clear();
-        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+    @NonNull
+    protected HeadsUpEntry createHeadsUpEntry() {
+        return new HeadsUpEntry();
     }
 
-    public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
-        mVisualStabilityManager = visualStabilityManager;
-    }
-
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
+    protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
+        // Do nothing for HeadsUpEntry.
     }
 
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
      */
-    public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
-        public NotificationData.Entry entry;
+    protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+        @Nullable public NotificationData.Entry entry;
         public long postTime;
-        public long earliestRemovaltime;
-        private Runnable mRemoveHeadsUpRunnable;
         public boolean remoteInputActive;
+        public long earliestRemovaltime;
         public boolean expanded;
 
-        public void setEntry(final NotificationData.Entry entry) {
+        private Runnable mRemoveHeadsUpRunnable;
+
+        public void setEntry(@Nullable final NotificationData.Entry entry) {
+            setEntry(entry, null);
+        }
+
+        public void setEntry(@Nullable final NotificationData.Entry entry,
+                @Nullable Runnable removeHeadsUpRunnable) {
             this.entry = entry;
+            this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
 
             // The actual post time will be just after the heads-up really slided in
             postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
-            mRemoveHeadsUpRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    if (!mVisualStabilityManager.isReorderingAllowed()) {
-                        mEntriesToRemoveWhenReorderingAllowed.add(entry);
-                        mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
-                    } else if (!mTrackingHeadsUp) {
-                        removeHeadsUpEntry(entry);
-                    } else {
-                        mEntriesToRemoveAfterExpand.add(entry);
-                    }
-                }
-            };
-            updateEntry();
-        }
-
-        public void updateEntry() {
-            updateEntry(true);
+            updateEntry(true /* updatePostTime */);
         }
 
         public void updateEntry(boolean updatePostTime) {
+            if (DEBUG) Log.v(TAG, "updateEntry");
+
             long currentTime = mClock.currentTimeMillis();
             earliestRemovaltime = currentTime + mMinimumDisplayTime;
             if (updatePostTime) {
                 postTime = Math.max(postTime, currentTime);
             }
             removeAutoRemovalCallbacks();
-            if (mEntriesToRemoveAfterExpand.contains(entry)) {
-                mEntriesToRemoveAfterExpand.remove(entry);
-            }
-            if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
-                mEntriesToRemoveWhenReorderingAllowed.remove(entry);
-            }
+
             if (!isSticky()) {
                 long finishTime = postTime + mHeadsUpNotificationDecay;
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -707,7 +493,7 @@
         }
 
         @Override
-        public int compareTo(HeadsUpEntry o) {
+        public int compareTo(@NonNull HeadsUpEntry o) {
             boolean isPinned = entry.row.isPinned();
             boolean otherPinned = o.entry.row.isPinned();
             if (isPinned && !otherPinned) {
@@ -734,26 +520,29 @@
                             : -1;
         }
 
-        public void removeAutoRemovalCallbacks() {
-            mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
-        }
-
-        public boolean wasShownLongEnough() {
-            return earliestRemovaltime < mClock.currentTimeMillis();
-        }
-
-        public void removeAsSoonAsPossible() {
-            removeAutoRemovalCallbacks();
-            mHandler.postDelayed(mRemoveHeadsUpRunnable,
-                    earliestRemovaltime - mClock.currentTimeMillis());
+        public void expanded(boolean expanded) {
+            this.expanded = expanded;
         }
 
         public void reset() {
-            removeAutoRemovalCallbacks();
             entry = null;
-            mRemoveHeadsUpRunnable = null;
             expanded = false;
             remoteInputActive = false;
+            removeAutoRemovalCallbacks();
+            mRemoveHeadsUpRunnable = null;
+        }
+
+        public void removeAutoRemovalCallbacks() {
+            if (mRemoveHeadsUpRunnable != null)
+                mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+        }
+
+        public void removeAsSoonAsPossible() {
+            if (mRemoveHeadsUpRunnable != null) {
+                removeAutoRemovalCallbacks();
+                mHandler.postDelayed(mRemoveHeadsUpRunnable,
+                        earliestRemovaltime - mClock.currentTimeMillis());
+            }
         }
     }
 
@@ -762,5 +551,4 @@
             return SystemClock.elapsedRealtime();
         }
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
new file mode 100644
index 0000000..1e3c123c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * A class of utility static methods for heads up notifications.
+ */
+public final class HeadsUpUtil {
+    private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
+
+    /**
+     * Set the given view as clicked or not-clicked.
+     * @param view The view to be set the flag to.
+     * @param clicked True to set as clicked. False to not-clicked.
+     */
+    public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
+        view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+    }
+
+    /**
+     * Check if the given view has the flag of "clicked notification"
+     * @param view The view to be checked.
+     * @return True if the view has clicked. False othrewise.
+     */
+    public static boolean isClickedHeadsUpNotification(View view) {
+        Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
+        return clicked != null && clicked;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 424858a..d7a810e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@
     private boolean mPanelTracking;
     private boolean mExpansionChanging;
     private boolean mPanelFullWidth;
-    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+    private boolean mPulsing;
     private boolean mUnlockHintRunning;
     private boolean mQsCustomizerShowing;
     private int mIntrinsicPadding;
@@ -315,23 +315,18 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing != null;
+        return mPulsing;
     }
 
-    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
+    public void setPulsing(boolean hasPulsing) {
         mPulsing = hasPulsing;
     }
 
     public boolean isPulsing(NotificationData.Entry entry) {
-        if (mPulsing == null) {
+        if (!mPulsing || mHeadsUpManager == null) {
             return false;
         }
-        for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
-            if (e.entry == entry) {
-                return true;
-            }
-        }
-        return false;
+        return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
     }
 
     public boolean isPanelTracking() {
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..1b55a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,10 +92,11 @@
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
 import android.support.v4.graphics.ColorUtils;
@@ -288,7 +289,7 @@
     private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
-    private HeadsUpManager mHeadsUpManager;
+    private HeadsUpManagerPhone mHeadsUpManager;
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -358,14 +359,14 @@
         }
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+    private boolean mPulsing;
     private boolean mDrawBackgroundAsSrc;
     private boolean mFadingOut;
     private boolean mParentNotFullyVisible;
     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 +403,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 +525,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 +550,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);
@@ -689,7 +691,7 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
-        if (mPulsing != null) {
+        if (mPulsing) {
             mTopPadding = mClockBottom;
         } else {
             mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -919,6 +921,27 @@
     }
 
     /**
+     * @return the height of the top heads up notification when pinned. This is different from the
+     *         intrinsic height, which also includes whether the notification is system expanded and
+     *         is mainly used when dragging down from a heads up notification.
+     */
+    private int getTopHeadsUpPinnedHeight() {
+        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+        if (topEntry == null) {
+            return 0;
+        }
+        ExpandableNotificationRow row = topEntry.row;
+        if (row.isChildInGroup()) {
+            final ExpandableNotificationRow groupSummary
+                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+            if (groupSummary != null) {
+                row = groupSummary;
+            }
+        }
+        return row.getPinnedHeadsUpHeight();
+    }
+
+    /**
      * @return the position from where the appear transition ends when expanding.
      *         Measured in absolute height.
      */
@@ -929,7 +952,7 @@
             int minNotificationsForShelf = 1;
             if (mTrackingHeadsUp
                     || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
-                appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
+                appearPosition = getTopHeadsUpPinnedHeight();
                 minNotificationsForShelf = 2;
             } else {
                 appearPosition = 0;
@@ -1197,9 +1220,9 @@
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().entry.row != row
+                            && mHeadsUpManager.getTopEntry().row != row
                             && mGroupManager.getGroupSummary(
-                                mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
+                                mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
                                 != row) {
                         continue;
                     }
@@ -2119,7 +2142,7 @@
 
     @Override
     public boolean hasPulsingNotifications() {
-        return mPulsing != null;
+        return mPulsing;
     }
 
     private void updateScrollability() {
@@ -2304,8 +2327,9 @@
             return;
         }
 
+        final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
         mScrimController.setExcludedBackgroundArea(
-                mFadingOut || mParentNotFullyVisible || mDarkAmount != 1 || mIsClipped ? null
+                mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
                         : mCurrentBounds);
         invalidate();
     }
@@ -2751,7 +2775,7 @@
     }
 
     private boolean isClickedHeadsUp(View child) {
-        return HeadsUpManager.isClickedHeadsUpNotification(child);
+        return HeadsUpUtil.isClickedHeadsUpNotification(child);
     }
 
     /**
@@ -3858,22 +3882,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 +3918,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();
@@ -4256,7 +4280,7 @@
         mAnimationFinishedRunnables.add(runnable);
     }
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
     }
@@ -4324,8 +4348,8 @@
         return mIsExpanded;
     }
 
-    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
-        if (mPulsing == null && pulsing == null) {
+    public void setPulsing(boolean pulsing, int clockBottom) {
+        if (!mPulsing && !pulsing) {
             return;
         }
         mPulsing = pulsing;
@@ -4455,15 +4479,16 @@
         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) {
         pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
                         + " alpha:%f scrollY:%d]",
                 this.getClass().getSimpleName(),
-                mPulsing != null ?"T":"f",
+                mPulsing ? "T":"f",
                 mAmbientState.isQsCustomizerShowing() ? "T":"f",
                 getVisibility() == View.VISIBLE ? "visible"
                         : getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 682b849..04a7bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
 
 /**
  * A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                HeadsUpManager.setIsClickedNotification(child, false);
+                HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
                 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
                 child.setTag(TAG_START_TRANSLATION_Y, null);
                 child.setTag(TAG_END_TRANSLATION_Y, null);
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 8cfdeeb..001a582 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -63,7 +63,6 @@
 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 +78,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;
@@ -111,7 +111,7 @@
     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 +134,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);
     }
@@ -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;
@@ -621,7 +625,7 @@
         }
     }
 
-    private void onStateChangedH(State state) {
+    protected void onStateChangedH(State state) {
         mState = state;
         mDynamic.clear();
         // add any new dynamic rows
@@ -894,7 +898,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 "";
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 066cfe5..936ff51 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -50,8 +50,8 @@
     android-slices-core \
     android-slices-view \
     android-slices-builders \
-    apptoolkit-arch-core-runtime \
-    apptoolkit-lifecycle-extensions \
+    android-arch-core-runtime \
+    android-arch-lifecycle-extensions \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     metrics-helper-lib \
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/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 6e7477f..f3c1171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationInflaterTest;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -51,7 +52,7 @@
     public NotificationTestHelper(Context context) {
         mContext = context;
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
+        mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
     }
 
     public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
new file mode 100644
index 0000000..28f9417
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.statusbar.phone;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.View;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class HeadsUpManagerPhoneTest extends SysuiTestCase {
+    @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+
+    private HeadsUpManagerPhone mHeadsUpManager;
+
+    private NotificationData.Entry mEntry;
+    private StatusBarNotification mSbn;
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private View mStatusBarWindowView;
+    @Mock private StatusBar mBar;
+    @Mock private ExpandableNotificationRow mRow;
+    @Mock private VisualStabilityManager mVSManager;
+
+    @Before
+    public void setUp() {
+        when(mVSManager.isReorderingAllowed()).thenReturn(true);
+
+        mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
+
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+             0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+
+        mEntry = new NotificationData.Entry(mSbn);
+        mEntry.row = mRow;
+        mEntry.expandedIcon = mock(StatusBarIconView.class);
+    }
+
+    @Test
+    public void testBasicOperations() {
+        // Check the initial state.
+        assertNull(mHeadsUpManager.getEntry(mEntry.key));
+        assertNull(mHeadsUpManager.getTopEntry());
+        assertEquals(0, mHeadsUpManager.getAllEntries().count());
+        assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+        // Add a notification.
+        mHeadsUpManager.showNotification(mEntry);
+
+        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+        assertEquals(1, mHeadsUpManager.getAllEntries().count());
+        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+        // Update the notification.
+        mHeadsUpManager.updateNotification(mEntry, false);
+
+        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+        assertEquals(1, mHeadsUpManager.getAllEntries().count());
+        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+        // Remove but defer, since the notification is visible on display.
+        mHeadsUpManager.removeNotification(mEntry.key, false);
+
+        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+        assertEquals(1, mHeadsUpManager.getAllEntries().count());
+        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index bdf9b1f..31442af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@
     @Mock private UnlockMethodCache mUnlockMethodCache;
     @Mock private KeyguardIndicationController mKeyguardIndicationController;
     @Mock private NotificationStackScrollLayout mStackScroller;
-    @Mock private HeadsUpManager mHeadsUpManager;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
     @Mock private SystemServicesProxy mSystemServicesProxy;
     @Mock private NotificationPanelView mNotificationPanelView;
     @Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
-                NotificationStackScrollLayout stack, HeadsUpManager hum,
+                NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
                 PowerManager pm, NotificationPanelView panelView,
                 IStatusBarService barService, NotificationListener notificationListener,
                 NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@
         public void setUpForTest(NotificationPresenter presenter,
                 NotificationListContainer listContainer,
                 Callback callback,
-                HeadsUpManager headsUpManager,
+                HeadsUpManagerPhone headsUpManager,
                 NotificationData notificationData) {
             super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
             mNotificationData = notificationData;
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/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
new file mode 100644
index 0000000..2d28c9f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 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 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.SysuiTestCase;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Predicate;
+
+@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);
+        VolumeDialogController.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);
+            state.states.append(i, ss);
+        }
+        mDialog.onStateChangedH(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);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
index 0fdbfd1..4ab2063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
@@ -30,15 +30,18 @@
 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 {
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..7539d88 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5168,6 +5168,11 @@
     // 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;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/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 fd3f708..44974ff 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1352,14 +1352,21 @@
         public int[] getPowerSaveTempWhitelistAppIds() {
             return DeviceIdleController.this.getAppIdTempWhitelistInternal();
         }
-
-        public void keyguardShowing(boolean showing) {
-            synchronized (DeviceIdleController.this) {
-                DeviceIdleController.this.keyguardShowingLocked(showing);
-            }
-        }
     }
 
+    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"));
@@ -1522,6 +1529,8 @@
                 mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
+                mLocalActivityManager.registerScreenObserver(mScreenObserver);
+
                 passWhiteListToForceAppStandbyTrackerLocked();
                 updateInteractivityLocked();
             }
@@ -2028,7 +2037,6 @@
         }
     }
 
-
     void scheduleReportActiveLocked(String activeReason, int activeUid) {
         Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
         mHandler.sendMessage(msg);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6f7d4b..5b8b691 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12912,7 +12912,6 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 mKeyguardController.setKeyguardShown(showing, secondaryDisplayShowing);
-                mLocalDeviceIdleController.keyguardShowing(showing);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -25425,6 +25424,7 @@
         public void notifyAppTransitionFinished() {
             synchronized (ActivityManagerService.this) {
                 mStackSupervisor.notifyAppTransitionDone();
+                mKeyguardController.notifyAppTransitionDone();
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3bef877..cae0d2b 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1695,7 +1695,7 @@
 
         resumeKeyDispatchingLocked();
         final ActivityStack stack = getStack();
-        stack.mNoAnimActivities.clear();
+        mStackSupervisor.mNoAnimActivities.clear();
 
         // Mark the point when the activity is resuming
         // TODO: To be more accurate, the mark should be before the onCreate,
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index fd3f8ec..ab2dc36 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -277,12 +277,6 @@
     final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
 
     /**
-     * Animations that for the current transition have requested not to
-     * be considered for the transition animation.
-     */
-    final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
-
-    /**
      * When we are in the process of pausing an activity, before starting the
      * next one, this variable holds the activity that is currently being paused.
      */
@@ -550,7 +544,7 @@
         wm.deferSurfaceLayout();
         try {
             if (!animate && topActivity != null) {
-                mNoAnimActivities.add(topActivity);
+                mStackSupervisor.mNoAnimActivities.add(topActivity);
             }
             super.setWindowingMode(windowingMode);
 
@@ -2460,7 +2454,7 @@
             if (prev.finishing) {
                 if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                         "Prepare close transition: prev=" + prev);
-                if (mNoAnimActivities.contains(prev)) {
+                if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
                     anim = false;
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
@@ -2472,7 +2466,7 @@
             } else {
                 if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                         "Prepare open transition: prev=" + prev);
-                if (mNoAnimActivities.contains(next)) {
+                if (mStackSupervisor.mNoAnimActivities.contains(next)) {
                     anim = false;
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
@@ -2485,7 +2479,7 @@
             }
         } else {
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
-            if (mNoAnimActivities.contains(next)) {
+            if (mStackSupervisor.mNoAnimActivities.contains(next)) {
                 anim = false;
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
             } else {
@@ -2493,17 +2487,14 @@
             }
         }
 
-        Bundle resumeAnimOptions = null;
         if (anim) {
-            ActivityOptions opts = next.getOptionsForTargetActivityLocked();
-            if (opts != null) {
-                resumeAnimOptions = opts.toBundle();
-            }
             next.applyOptionsLocked();
         } else {
             next.clearOptionsLocked();
         }
 
+        mStackSupervisor.mNoAnimActivities.clear();
+
         ActivityStack lastStack = mStackSupervisor.getLastStack();
         if (next.app != null && next.app.thread != null) {
             if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
@@ -2859,7 +2850,7 @@
                     "Prepare open transition: starting " + r);
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
-                mNoAnimActivities.add(r);
+                mStackSupervisor.mNoAnimActivities.add(r);
             } else {
                 int transit = TRANSIT_ACTIVITY_OPEN;
                 if (newTask) {
@@ -2878,7 +2869,7 @@
                     }
                 }
                 mWindowManager.prepareAppTransition(transit, keepCurTransition);
-                mNoAnimActivities.remove(r);
+                mStackSupervisor.mNoAnimActivities.remove(r);
             }
             boolean doShow = true;
             if (newTask) {
@@ -4497,7 +4488,7 @@
         if (noAnimation) {
             mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
             if (r != null) {
-                mNoAnimActivities.add(r);
+                mStackSupervisor.mNoAnimActivities.add(r);
             }
             ActivityOptions.abort(options);
         } else {
@@ -5190,7 +5181,6 @@
 
     void executeAppTransition(ActivityOptions options) {
         mWindowManager.executeAppTransition();
-        mNoAnimActivities.clear();
         ActivityOptions.abort(options);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7c9160a..6beafcb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -357,6 +357,12 @@
      * application */
     final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
 
+    /**
+     * Animations that for the current transition have requested not to
+     * be considered for the transition animation.
+     */
+    final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
+
     /** The target stack bounds for the picture-in-picture mode changed that we need to report to
      * the application */
     Rect mPipModeChangedTargetStackBounds;
@@ -1245,14 +1251,10 @@
         synchronized (mService) {
             try {
                 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
-                int modifiedFlags = flags
-                        | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
-                if (intent.isBrowsableWebIntent()
-                            || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
-                    modifiedFlags |= PackageManager.MATCH_INSTANT;
-                }
                 return mService.getPackageManagerInternalLocked().resolveIntent(
-                        intent, resolvedType, modifiedFlags, userId, true);
+                        intent, resolvedType, PackageManager.MATCH_INSTANT
+                                | PackageManager.MATCH_DEFAULT_ONLY | flags
+                                | ActivityManagerService.STOCK_PM_FLAGS, userId, true);
 
             } finally {
                 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eab88aa..8fd754a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -31,7 +31,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -790,7 +789,7 @@
         // Instead, launch the ephemeral installer. Once the installer is finished, it
         // starts either the intent we resolved here [on install error] or the ephemeral
         // app [on install success].
-        if (rInfo != null && rInfo.isInstantAppAvailable) {
+        if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
                     callingPackage, verificationBundle, resolvedType, userId);
             resolvedType = null;
@@ -850,27 +849,22 @@
     /**
      * Creates a launch intent for the given auxiliary resolution data.
      */
-    private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
+    private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse,
             Intent originalIntent, String callingPackage, Bundle verificationBundle,
             String resolvedType, int userId) {
-        if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
+        if (auxiliaryResponse.needsPhaseTwo) {
             // request phase two resolution
             mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
                     auxiliaryResponse, originalIntent, resolvedType, callingPackage,
                     verificationBundle, userId);
         }
         return InstantAppResolver.buildEphemeralInstallerIntent(
-                originalIntent,
-                InstantAppResolver.sanitizeIntent(originalIntent),
-                auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
-                callingPackage,
-                verificationBundle,
-                resolvedType,
-                userId,
-                auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity,
-                auxiliaryResponse == null ? null : auxiliaryResponse.token,
-                auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo,
-                auxiliaryResponse == null ? null : auxiliaryResponse.filters);
+                Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, originalIntent,
+                auxiliaryResponse.failureIntent, callingPackage, verificationBundle,
+                resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName,
+                auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode,
+                auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(),
+                auxiliaryResponse.needsPhaseTwo);
     }
 
     void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
@@ -930,12 +924,12 @@
         // Don't modify the client's object!
         intent = new Intent(intent);
         if (componentSpecified
-                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
-                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
+                && intent.getData() != null
+                && Intent.ACTION_VIEW.equals(intent.getAction())
                 && mService.getPackageManagerInternalLocked()
                         .isInstantAppInstallerComponent(intent.getComponent())) {
             // intercept intents targeted directly to the ephemeral installer the
-            // ephemeral installer should never be started with a raw Intent; instead
+            // ephemeral installer should never be started with a raw URL; instead
             // adjust the intent so it looks like a "normal" instant app launch
             intent.setComponent(null /*component*/);
             componentSpecified = false;
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/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index c188ccb..e7b067b 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -150,7 +150,7 @@
                     if (moveHomeToTop) {
                         // Bring the home stack to the front
                         final ActivityStack homeStack = homeActivity.getStack();
-                        homeStack.mNoAnimActivities.add(homeActivity);
+                        mStackSupervisor.mNoAnimActivities.add(homeActivity);
                         homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
                     } else {
                         // Restore the home stack to its previous position
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 809f19f6..d679439 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -695,7 +695,7 @@
                         wasPaused, reason);
             }
             if (!animate) {
-                toStack.mNoAnimActivities.add(topActivity);
+                mService.mStackSupervisor.mNoAnimActivities.add(topActivity);
             }
 
             // We might trigger a configuration change. Save the current task bounds for freezing.
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/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index bd2e96e..e43d152 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -150,7 +150,8 @@
             fillLinkInfo(ev, newNai);
             ev.initialScore = newNai.getCurrentScore();
             if (newNai.lastValidated) {
-                logDefaultNetworkValidity(timeMs, true);
+                mIsCurrentlyValid = true;
+                mLastValidationTimeMs = timeMs;
             }
         } else {
             mIsCurrentlyValid = false;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 25a2100..b5f94b1 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -421,7 +421,7 @@
                 byteToken[i] = token.get(i);
             }
             // Send to Keystore
-            KeyStore.getInstance().addAuthToken(byteToken, mCurrentUserId);
+            KeyStore.getInstance().addAuthToken(byteToken);
         }
         if (client != null && client.onAuthenticated(fingerId, groupId)) {
             removeClient(client);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 8fa3318..e0baeee 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2206,12 +2206,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/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f4bbf4c..cf27882 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -62,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by
@@ -85,7 +86,7 @@
     private static final int MAX_OPS_BEFORE_WRITE = 1;
 
     final Object mLock;
-    final JobSet mJobSet; // per-caller-uid tracking
+    final JobSet mJobSet; // per-caller-uid and per-source-uid tracking
     final Context mContext;
 
     // Bookkeeping around incorrect boot-time system clock
@@ -1000,10 +1001,11 @@
     }
 
     static final class JobSet {
-        // Key is the getUid() originator of the jobs in each sheaf
-        private SparseArray<ArraySet<JobStatus>> mJobs;
-        // Same data but with the key as getSourceUid() of the jobs in each sheaf
-        private SparseArray<ArraySet<JobStatus>> mJobsPerSourceUid;
+        @VisibleForTesting // Key is the getUid() originator of the jobs in each sheaf
+        final SparseArray<ArraySet<JobStatus>> mJobs;
+
+        @VisibleForTesting // Same data but with the key as getSourceUid() of the jobs in each sheaf
+        final SparseArray<ArraySet<JobStatus>> mJobsPerSourceUid;
 
         public JobSet() {
             mJobs = new SparseArray<ArraySet<JobStatus>>();
@@ -1046,7 +1048,13 @@
                 jobsForSourceUid = new ArraySet<>();
                 mJobsPerSourceUid.put(sourceUid, jobsForSourceUid);
             }
-            return jobs.add(job) && jobsForSourceUid.add(job);
+            final boolean added = jobs.add(job);
+            final boolean addedInSource = jobsForSourceUid.add(job);
+            if (added != addedInSource) {
+                Slog.wtf(TAG, "mJobs and mJobsPerSourceUid mismatch; caller= " + added
+                        + " source= " + addedInSource);
+            }
+            return added || addedInSource;
         }
 
         public boolean remove(JobStatus job) {
@@ -1075,27 +1083,40 @@
 
         /**
          * Removes the jobs of all users not specified by the whitelist of user ids.
-         * The jobs scheduled by non existent users will not be removed if they were
+         * This will remove jobs scheduled *by* non-existent users as well as jobs scheduled *for*
+         * non-existent users
          */
-        public void removeJobsOfNonUsers(int[] whitelist) {
-            for (int jobSetIndex = mJobsPerSourceUid.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
-                final int jobUserId = UserHandle.getUserId(mJobsPerSourceUid.keyAt(jobSetIndex));
-                if (!ArrayUtils.contains(whitelist, jobUserId)) {
-                    mJobsPerSourceUid.removeAt(jobSetIndex);
-                }
-            }
+        public void removeJobsOfNonUsers(final int[] whitelist) {
+            final Predicate<JobStatus> noSourceUser =
+                    job -> !ArrayUtils.contains(whitelist, job.getSourceUserId());
+            final Predicate<JobStatus> noCallingUser =
+                    job -> !ArrayUtils.contains(whitelist, job.getUserId());
+            removeAll(noSourceUser.or(noCallingUser));
+        }
+
+        private void removeAll(Predicate<JobStatus> predicate) {
             for (int jobSetIndex = mJobs.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
-                final ArraySet<JobStatus> jobsForUid = mJobs.valueAt(jobSetIndex);
-                for (int jobIndex = jobsForUid.size() - 1; jobIndex >= 0; jobIndex--) {
-                    final int jobUserId = jobsForUid.valueAt(jobIndex).getUserId();
-                    if (!ArrayUtils.contains(whitelist, jobUserId)) {
-                        jobsForUid.removeAt(jobIndex);
+                final ArraySet<JobStatus> jobs = mJobs.valueAt(jobSetIndex);
+                for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
+                    if (predicate.test(jobs.valueAt(jobIndex))) {
+                        jobs.removeAt(jobIndex);
                     }
                 }
-                if (jobsForUid.size() == 0) {
+                if (jobs.size() == 0) {
                     mJobs.removeAt(jobSetIndex);
                 }
             }
+            for (int jobSetIndex = mJobsPerSourceUid.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
+                final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(jobSetIndex);
+                for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
+                    if (predicate.test(jobs.valueAt(jobIndex))) {
+                        jobs.removeAt(jobIndex);
+                    }
+                }
+                if (jobs.size() == 0) {
+                    mJobsPerSourceUid.removeAt(jobSetIndex);
+                }
+            }
         }
 
         public boolean contains(JobStatus job) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index cfa1a79..ec72b22 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -458,28 +458,22 @@
             throws RemoteException, ServiceSpecificException {
         byte[] locallyEncryptedKey;
         try {
+            // TODO: Remove the extraneous logging here
+            Log.e(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
+                    sessionEntry.getKeyClaimant()));
+            Log.e(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
+                    sessionEntry.getVaultParams()));
+            Log.e(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
             locallyEncryptedKey = KeySyncUtils.decryptRecoveryClaimResponse(
                     sessionEntry.getKeyClaimant(),
                     sessionEntry.getVaultParams(),
                     encryptedClaimResponse);
         } catch (InvalidKeyException e) {
-            // TODO: Remove the extraneous logging here
             Log.e(TAG, "Got InvalidKeyException during decrypting recovery claim response", e);
-            Log.e(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
-                    sessionEntry.getKeyClaimant()));
-            Log.e(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
-                    sessionEntry.getVaultParams()));
-            Log.e(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
             throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                     "Failed to decrypt recovery key " + e.getMessage());
         } catch (AEADBadTagException e) {
-            // TODO: Remove the extraneous logging here
             Log.e(TAG, "Got AEADBadTagException during decrypting recovery claim response", e);
-            Log.e(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
-                    sessionEntry.getKeyClaimant()));
-            Log.e(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
-                    sessionEntry.getVaultParams()));
-            Log.e(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
             throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                     "Failed to decrypt recovery key " + e.getMessage());
         } catch (NoSuchAlgorithmException e) {
@@ -488,21 +482,17 @@
         }
 
         try {
-            return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
-        } catch (InvalidKeyException e) {
             // TODO: Remove the extraneous logging here
-            Log.e(TAG, "Got InvalidKeyException during decrypting recovery key", e);
             Log.e(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
                     sessionEntry.getLskfHash()));
             Log.e(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
+            return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
+        } catch (InvalidKeyException e) {
+            Log.e(TAG, "Got InvalidKeyException during decrypting recovery key", e);
             throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                     "Failed to decrypt recovery key " + e.getMessage());
         } catch (AEADBadTagException e) {
-            // TODO: Remove the extraneous logging here
             Log.e(TAG, "Got AEADBadTagException during decrypting recovery key", e);
-            Log.e(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
-                    sessionEntry.getLskfHash()));
-            Log.e(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
             throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                     "Failed to decrypt recovery key " + e.getMessage());
         } catch (NoSuchAlgorithmException e) {
@@ -534,6 +524,9 @@
             byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
 
             try {
+                // TODO: Remove the extraneous logging here
+                Log.e(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
+                Log.e(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
                 byte[] keyMaterial =
                         KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
                 keyMaterialByAlias.put(alias, keyMaterial);
@@ -542,19 +535,14 @@
                 throw new ServiceSpecificException(
                         ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
             } catch (InvalidKeyException e) {
-                // TODO: Remove the extraneous logging here
                 Log.e(TAG, "Got InvalidKeyException during decrypting application key with alias: "
                         + alias, e);
-                Log.e(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
-                Log.e(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
                 throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                         "Failed to recover key with alias '" + alias + "': " + e.getMessage());
             } catch (AEADBadTagException e) {
                 // TODO: Remove the extraneous logging here
                 Log.e(TAG, "Got AEADBadTagException during decrypting application key with alias: "
                         + alias, e);
-                Log.e(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
-                Log.e(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
                 throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                         "Failed to recover key with alias '" + alias + "': " + e.getMessage());
             }
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/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/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 6d6c960..b5ddf8c 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -43,7 +43,6 @@
 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;
@@ -85,8 +84,8 @@
         mIntent = new Intent(action).setComponent(componentName);
     }
 
-    public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(Intent sanitizedIntent,
-            int hashPrefix[], String token) throws ConnectionException {
+    public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
+            String token) throws ConnectionException {
         throwIfCalledOnMainThread();
         IInstantAppResolver target = null;
         try {
@@ -99,7 +98,7 @@
             }
             try {
                 return mGetEphemeralResolveInfoCaller
-                        .getEphemeralResolveInfoList(target, sanitizedIntent, hashPrefix, token);
+                        .getEphemeralResolveInfoList(target, hashPrefix, token);
             } catch (TimeoutException e) {
                 throw new ConnectionException(ConnectionException.FAILURE_CALL);
             } catch (RemoteException ignore) {
@@ -112,22 +111,26 @@
         return null;
     }
 
-    public final void getInstantAppIntentFilterList(Intent sanitizedIntent, int hashPrefix[],
-            String token, PhaseTwoCallback callback, Handler callbackHandler, final long startTime)
-            throws ConnectionException {
+    public final void getInstantAppIntentFilterList(int hashPrefix[], String token,
+            String hostName, PhaseTwoCallback callback, Handler callbackHandler,
+            final long startTime) throws ConnectionException {
         final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() {
             @Override
             public void sendResult(Bundle data) throws RemoteException {
                 final ArrayList<InstantAppResolveInfo> resolveList =
                         data.getParcelableArrayList(
                                 InstantAppResolverService.EXTRA_RESOLVE_INFO);
-                callbackHandler.post(() -> callback.onPhaseTwoResolved(resolveList, startTime));
+                callbackHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onPhaseTwoResolved(resolveList, startTime);
+                    }
+                });
             }
         };
         try {
             getRemoteInstanceLazy(token)
-                    .getInstantAppIntentFilterList(sanitizedIntent, hashPrefix, token,
-                            remoteCallback);
+                    .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback);
         } catch (TimeoutException e) {
             throw new ConnectionException(ConnectionException.FAILURE_BIND);
         } catch (InterruptedException e) {
@@ -334,11 +337,10 @@
         }
 
         public List<InstantAppResolveInfo> getEphemeralResolveInfoList(
-                IInstantAppResolver target, Intent sanitizedIntent,  int hashPrefix[], String token)
+                IInstantAppResolver target, int hashPrefix[], String token)
                         throws RemoteException, TimeoutException {
             final int sequence = onBeforeRemoteCall();
-            target.getInstantAppResolveInfoList(sanitizedIntent, hashPrefix, token, sequence,
-                    mCallback);
+            target.getInstantAppResolveInfoList(hashPrefix, token, sequence, mCallback);
             return getResultTimed(sequence);
         }
     }
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..30072d4 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -40,14 +40,11 @@
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
 import android.metrics.LogMaker;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -56,12 +53,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 
 /** @hide */
@@ -86,7 +79,6 @@
     public @interface ResolutionStatus {}
 
     private static MetricsLogger sMetricsLogger;
-
     private static MetricsLogger getLogger() {
         if (sMetricsLogger == null) {
             sMetricsLogger = new MetricsLogger();
@@ -94,49 +86,26 @@
         return sMetricsLogger;
     }
 
-    /**
-     * Returns an intent with potential PII removed from the original intent. Fields removed
-     * include extras and the host + path of the data, if defined.
-     */
-    public static Intent sanitizeIntent(Intent origIntent) {
-        final Intent sanitizedIntent;
-        sanitizedIntent = new Intent(origIntent.getAction());
-        Set<String> categories = origIntent.getCategories();
-        if (categories != null) {
-            for (String category : categories) {
-                sanitizedIntent.addCategory(category);
-            }
-        }
-        Uri sanitizedUri = origIntent.getData() == null
-                ? null
-                : Uri.fromParts(origIntent.getScheme(), "", "");
-        sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType());
-        sanitizedIntent.addFlags(origIntent.getFlags());
-        sanitizedIntent.setPackage(origIntent.getPackage());
-        return sanitizedIntent;
-    }
-
-    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
+    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context,
             EphemeralResolverConnection connection, InstantAppRequest requestObj) {
         final long startTime = System.currentTimeMillis();
         final String token = UUID.randomUUID().toString();
         if (DEBUG_EPHEMERAL) {
             Log.d(TAG, "[" + token + "] Phase1; resolving");
         }
-        final Intent origIntent = requestObj.origIntent;
-        final Intent sanitizedIntent = sanitizeIntent(origIntent);
-
-        final InstantAppDigest digest = getInstantAppDigest(origIntent);
+        final Intent intent = requestObj.origIntent;
+        final InstantAppDigest digest =
+                new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/);
         final int[] shaPrefix = digest.getDigestPrefix();
         AuxiliaryResolveInfo resolveInfo = null;
         @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
         try {
             final List<InstantAppResolveInfo> instantAppResolveInfoList =
-                    connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token);
+                    connection.getInstantAppResolveInfoList(shaPrefix, token);
             if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                 resolveInfo = InstantAppResolver.filterInstantAppIntent(
-                        instantAppResolveInfoList, origIntent, requestObj.resolvedType,
-                        requestObj.userId, origIntent.getPackage(), digest, token);
+                        instantAppResolveInfoList, intent, requestObj.resolvedType,
+                        requestObj.userId, intent.getPackage(), digest, token);
             }
         } catch (ConnectionException e) {
             if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -166,12 +135,6 @@
         return resolveInfo;
     }
 
-    private static InstantAppDigest getInstantAppDigest(Intent origIntent) {
-        return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())
-                ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/)
-                : InstantAppDigest.UNDEFINED;
-    }
-
     public static void doInstantAppResolutionPhaseTwo(Context context,
             EphemeralResolverConnection connection, InstantAppRequest requestObj,
             ActivityInfo instantAppInstaller, Handler callbackHandler) {
@@ -180,53 +143,73 @@
         if (DEBUG_EPHEMERAL) {
             Log.d(TAG, "[" + token + "] Phase2; resolving");
         }
-        final Intent origIntent = requestObj.origIntent;
-        final Intent sanitizedIntent = sanitizeIntent(origIntent);
-        final InstantAppDigest digest = getInstantAppDigest(origIntent);
+        final Intent intent = requestObj.origIntent;
+        final String hostName = intent.getData().getHost();
+        final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/);
         final int[] shaPrefix = digest.getDigestPrefix();
 
         final PhaseTwoCallback callback = new PhaseTwoCallback() {
             @Override
             void onPhaseTwoResolved(List<InstantAppResolveInfo> instantAppResolveInfoList,
                     long startTime) {
+                final String packageName;
+                final String splitName;
+                final long versionCode;
                 final Intent failureIntent;
+                final Bundle extras;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
                             InstantAppResolver.filterInstantAppIntent(
-                                    instantAppResolveInfoList, origIntent, null /*resolvedType*/,
-                                    0 /*userId*/, origIntent.getPackage(), digest, token);
-                    if (instantAppIntentInfo != null) {
+                                    instantAppResolveInfoList, intent, null /*resolvedType*/,
+                                    0 /*userId*/, intent.getPackage(), digest, token);
+                    if (instantAppIntentInfo != null
+                            && instantAppIntentInfo.resolveInfo != null) {
+                        packageName = instantAppIntentInfo.resolveInfo.getPackageName();
+                        splitName = instantAppIntentInfo.splitName;
+                        versionCode = instantAppIntentInfo.resolveInfo.getVersionCode();
                         failureIntent = instantAppIntentInfo.failureIntent;
+                        extras = instantAppIntentInfo.resolveInfo.getExtras();
                     } else {
+                        packageName = null;
+                        splitName = null;
+                        versionCode = -1;
                         failureIntent = null;
+                        extras = null;
                     }
                 } else {
+                    packageName = null;
+                    splitName = null;
+                    versionCode = -1;
                     failureIntent = null;
+                    extras = null;
                 }
                 final Intent installerIntent = buildEphemeralInstallerIntent(
+                        Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE,
                         requestObj.origIntent,
-                        sanitizedIntent,
                         failureIntent,
                         requestObj.callingPackage,
                         requestObj.verificationBundle,
                         requestObj.resolvedType,
                         requestObj.userId,
+                        packageName,
+                        splitName,
                         requestObj.responseObj.installFailureActivity,
+                        versionCode,
                         token,
-                        false /*needsPhaseTwo*/,
-                        requestObj.responseObj.filters);
+                        extras,
+                        false /*needsPhaseTwo*/);
                 installerIntent.setComponent(new ComponentName(
                         instantAppInstaller.packageName, instantAppInstaller.name));
 
                 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
-                        requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
+                        packageName != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
 
                 context.startActivity(installerIntent);
             }
         };
         try {
-            connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback,
-                    callbackHandler, startTime);
+            connection.getInstantAppIntentFilterList(
+                    shaPrefix, token, hostName, callback, callbackHandler, startTime);
         } catch (ConnectionException e) {
             @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
             if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -248,20 +231,23 @@
      * Builds and returns an intent to launch the instant installer.
      */
     public static Intent buildEphemeralInstallerIntent(
+            @NonNull String action,
             @NonNull Intent origIntent,
-            @NonNull Intent sanitizedIntent,
-            @Nullable Intent failureIntent,
+            @NonNull Intent failureIntent,
             @NonNull String callingPackage,
             @Nullable Bundle verificationBundle,
             @NonNull String resolvedType,
             int userId,
+            @NonNull String instantAppPackageName,
+            @Nullable String instantAppSplitName,
             @Nullable ComponentName installFailureActivity,
+            long versionCode,
             @Nullable String token,
-            boolean needsPhaseTwo,
-            List<AuxiliaryResolveInfo.AuxiliaryFilter> filters) {
+            @Nullable Bundle extras,
+            boolean needsPhaseTwo) {
         // Construct the intent that launches the instant installer
         int flags = origIntent.getFlags();
-        final Intent intent = new Intent();
+        final Intent intent = new Intent(action);
         intent.setFlags(flags
                 | Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_CLEAR_TASK
@@ -274,23 +260,20 @@
             intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
         }
         intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
-        intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent);
+        if (extras != null) {
+            intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras);
+        }
 
-        if (needsPhaseTwo) {
-            intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
-        } else {
-            // We have all of the data we need; just start the installer without a second phase
+        // We have all of the data we need; just start the installer without a second phase
+        if (!needsPhaseTwo) {
+            // Intent that is launched if the package couldn't be installed for any reason.
             if (failureIntent != null || installFailureActivity != null) {
-                // Intent that is launched if the package couldn't be installed for any reason.
                 try {
                     final Intent onFailureIntent;
                     if (installFailureActivity != null) {
                         onFailureIntent = new Intent();
                         onFailureIntent.setComponent(installFailureActivity);
-                        if (filters != null && filters.size() == 1) {
-                            onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME,
-                                    filters.get(0).splitName);
-                        }
+                        onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
                         onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent);
                     } else {
                         onFailureIntent = failureIntent;
@@ -326,35 +309,17 @@
                 intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS,
                         new IntentSender(successIntentTarget));
             } catch (RemoteException ignore) { /* ignore; same process */ }
+
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName);
+            intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
+            intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff));
+            intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode);
+            intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
             if (verificationBundle != null) {
                 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
             }
-            intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
-
-            if (filters != null) {
-                Bundle resolvableFilters[] = new Bundle[filters.size()];
-                for (int i = 0, max = filters.size(); i < max; i++) {
-                    Bundle resolvableFilter = new Bundle();
-                    AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i);
-                    resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP,
-                            filter.resolveInfo != null
-                                    && filter.resolveInfo.shouldLetInstallerDecide());
-                    resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName);
-                    resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName);
-                    resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode);
-                    resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras);
-                    resolvableFilters[i] = resolvableFilter;
-                    if (i == 0) {
-                        // for backwards compat, always set the first result on the intent and add
-                        // the int version code
-                        intent.putExtras(resolvableFilter);
-                        intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode);
-                    }
-                }
-                intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters);
-            }
-            intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
         }
+
         return intent;
     }
 
@@ -365,134 +330,69 @@
         final int[] shaPrefix = digest.getDigestPrefix();
         final byte[][] digestBytes = digest.getDigestBytes();
         final Intent failureIntent = new Intent(origIntent);
-        boolean requiresSecondPhase = false;
         failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
         failureIntent.setLaunchToken(token);
-        ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null;
-        boolean isWebIntent = origIntent.isBrowsableWebIntent();
-        for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) {
-            if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) {
-                Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest"
-                        + " provided; ignoring");
-                continue;
-            }
-            byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes();
-            // Only include matching digests if we have a prefix and we're either dealing with a
-            // web intent or the resolveInfo specifies digest details.
-            if (shaPrefix.length > 0 && (isWebIntent || filterDigestBytes.length > 0)) {
-                boolean matchFound = false;
-                // Go in reverse order so we match the narrowest scope first.
-                for (int i = shaPrefix.length - 1; i >= 0; --i) {
-                    if (Arrays.equals(digestBytes[i], filterDigestBytes)) {
-                        matchFound = true;
-                        break;
+        // Go in reverse order so we match the narrowest scope first.
+        for (int i = shaPrefix.length - 1; i >= 0 ; --i) {
+            for (InstantAppResolveInfo instantAppInfo : instantAppResolveInfoList) {
+                if (!Arrays.equals(digestBytes[i], instantAppInfo.getDigestBytes())) {
+                    continue;
+                }
+                if (packageName != null
+                        && !packageName.equals(instantAppInfo.getPackageName())) {
+                    continue;
+                }
+                final List<InstantAppIntentFilter> instantAppFilters =
+                        instantAppInfo.getIntentFilters();
+                // No filters; we need to start phase two
+                if (instantAppFilters == null || instantAppFilters.isEmpty()) {
+                    if (DEBUG_EPHEMERAL) {
+                        Log.d(TAG, "No app filters; go to phase 2");
+                    }
+                    return new AuxiliaryResolveInfo(instantAppInfo,
+                            new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/,
+                            null /*splitName*/, token, true /*needsPhase2*/,
+                            null /*failureIntent*/);
+                }
+                // We have a domain match; resolve the filters to see if anything matches.
+                final PackageManagerService.EphemeralIntentResolver instantAppResolver =
+                        new PackageManagerService.EphemeralIntentResolver();
+                for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
+                    final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
+                    final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
+                    if (splitFilters == null || splitFilters.isEmpty()) {
+                        continue;
+                    }
+                    for (int k = splitFilters.size() - 1; k >= 0; --k) {
+                        final AuxiliaryResolveInfo intentInfo =
+                                new AuxiliaryResolveInfo(instantAppInfo,
+                                        splitFilters.get(k), instantAppFilter.getSplitName(),
+                                        token, false /*needsPhase2*/, failureIntent);
+                        instantAppResolver.addFilter(intentInfo);
                     }
                 }
-                if (!matchFound) {
-                    continue;
+                List<AuxiliaryResolveInfo> matchedResolveInfoList = instantAppResolver.queryIntent(
+                        origIntent, resolvedType, false /*defaultOnly*/, userId);
+                if (!matchedResolveInfoList.isEmpty()) {
+                    if (DEBUG_EPHEMERAL) {
+                        final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0);
+                        Log.d(TAG, "[" + token + "] Found match;"
+                                + " package: " + info.packageName
+                                + ", split: " + info.splitName
+                                + ", versionCode: " + info.versionCode);
+                    }
+                    return matchedResolveInfoList.get(0);
+                } else if (DEBUG_EPHEMERAL) {
+                    Log.d(TAG, "[" + token + "] No matches found"
+                            + " package: " + instantAppInfo.getPackageName()
+                            + ", versionCode: " + instantAppInfo.getVersionCode());
                 }
             }
-            // We matched a resolve info; resolve the filters to see if anything matches completely.
-            List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
-                    origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
-            if (matchFilters != null) {
-                if (matchFilters.isEmpty()) {
-                    requiresSecondPhase = true;
-                }
-                if (filters == null) {
-                    filters = new ArrayList<>(matchFilters);
-                } else {
-                    filters.addAll(matchFilters);
-                }
-            }
-        }
-        if (filters != null && !filters.isEmpty()) {
-            return new AuxiliaryResolveInfo(token, requiresSecondPhase, failureIntent, filters);
         }
         // Hash or filter mis-match; no instant apps for this domain.
         return null;
     }
 
-    /**
-     * Returns one of three states: <p/>
-     * <ul>
-     *     <li>{@code null} if there are no matches will not be; resolution is unnecessary.</li>
-     *     <li>An empty list signifying that a 2nd phase of resolution is required.</li>
-     *     <li>A populated list meaning that matches were found and should be sent directly to the
-     *     installer</li>
-     * </ul>
-     *
-     */
-    private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
-            Intent origIntent, String resolvedType, int userId, String packageName, String token,
-            InstantAppResolveInfo instantAppInfo) {
-        if (instantAppInfo.shouldLetInstallerDecide()) {
-            return Collections.singletonList(
-                    new AuxiliaryResolveInfo.AuxiliaryFilter(
-                            instantAppInfo, null /* splitName */,
-                            instantAppInfo.getExtras()));
-        }
-        if (packageName != null
-                && !packageName.equals(instantAppInfo.getPackageName())) {
-            return null;
-        }
-        final List<InstantAppIntentFilter> instantAppFilters =
-                instantAppInfo.getIntentFilters();
-        if (instantAppFilters == null || instantAppFilters.isEmpty()) {
-            // No filters on web intent; no matches, 2nd phase unnecessary.
-            if (origIntent.isBrowsableWebIntent()) {
-                return null;
-            }
-            // No filters; we need to start phase two
-            if (DEBUG_EPHEMERAL) {
-                Log.d(TAG, "No app filters; go to phase 2");
-            }
-            return Collections.emptyList();
-        }
-        final PackageManagerService.EphemeralIntentResolver instantAppResolver =
-                new PackageManagerService.EphemeralIntentResolver();
-        for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
-            final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
-            final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
-            if (splitFilters == null || splitFilters.isEmpty()) {
-                continue;
-            }
-            for (int k = splitFilters.size() - 1; k >= 0; --k) {
-                IntentFilter filter = splitFilters.get(k);
-                Iterator<IntentFilter.AuthorityEntry> authorities =
-                        filter.authoritiesIterator();
-                // ignore http/s-only filters.
-                if ((authorities == null || !authorities.hasNext())
-                        && (filter.hasDataScheme("http") || filter.hasDataScheme("https"))
-                        && filter.hasAction(Intent.ACTION_VIEW)
-                        && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
-                    continue;
-                }
-                instantAppResolver.addFilter(
-                        new AuxiliaryResolveInfo.AuxiliaryFilter(
-                                filter,
-                                instantAppInfo,
-                                instantAppFilter.getSplitName(),
-                                instantAppInfo.getExtras()
-                        ));
-            }
-        }
-        List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
-                instantAppResolver.queryIntent(
-                        origIntent, resolvedType, false /*defaultOnly*/, userId);
-        if (!matchedResolveInfoList.isEmpty()) {
-            if (DEBUG_EPHEMERAL) {
-                Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
-            }
-            return matchedResolveInfoList;
-        } else if (DEBUG_EPHEMERAL) {
-            Log.d(TAG, "[" + token + "] No matches found"
-                    + " package: " + instantAppInfo.getPackageName()
-                    + ", versionCode: " + instantAppInfo.getVersionCode());
-        }
-        return null;
-    }
-
     private static void logMetrics(int action, long startTime, String token,
             @ResolutionStatus int status) {
         final LogMaker logMaker = new LogMaker(action)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fdb157e..01c44af 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;
@@ -3595,35 +3594,24 @@
     }
 
     private @Nullable ActivityInfo getInstantAppInstallerLPr() {
-        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}
-                : new String[]{
-                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE,
-                        Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE};
+        final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
 
         final int resolveFlags =
                 MATCH_DIRECT_BOOT_AWARE
-                        | MATCH_DIRECT_BOOT_UNAWARE
-                        | Intent.FLAG_IGNORE_EPHEMERAL
-                        | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0);
-        final Intent intent = new Intent();
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
-        List<ResolveInfo> matches = null;
-        for (String action : orderedActions) {
-            intent.setAction(action);
+                | MATCH_DIRECT_BOOT_UNAWARE
+                | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
+        List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+                resolveFlags, UserHandle.USER_SYSTEM);
+        // temporarily look for the old action
+        if (matches.isEmpty()) {
+            if (DEBUG_EPHEMERAL) {
+                Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
+            }
+            intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
             matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                     resolveFlags, UserHandle.USER_SYSTEM);
-            if (matches.isEmpty()) {
-                if (DEBUG_EPHEMERAL) {
-                    Slog.d(TAG, "Instant App installer not found with " + action);
-                }
-            } else {
-                break;
-            }
         }
         Iterator<ResolveInfo> iter = matches.iterator();
         while (iter.hasNext()) {
@@ -3631,8 +3619,7 @@
             final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName);
             if (ps != null) {
                 final PermissionsState permissionsState = ps.getPermissionsState();
-                if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)
-                        || Build.IS_ENG) {
+                if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)) {
                     continue;
                 }
             }
@@ -4791,7 +4778,10 @@
             flags |= PackageManager.MATCH_INSTANT;
         } else {
             final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
-            final boolean allowMatchInstant = wantInstantApps
+            final boolean allowMatchInstant =
+                    (wantInstantApps
+                            && Intent.ACTION_VIEW.equals(intent.getAction())
+                            && hasWebURI(intent))
                     || (wantMatchInstant && canViewInstantApps(callingUid, userId));
             flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
                     | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
@@ -5546,9 +5536,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 +5577,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;
             }
@@ -5981,14 +5971,8 @@
         if (!skipPackageCheck && intent.getPackage() != null) {
             return false;
         }
-        if (!intent.isBrowsableWebIntent()) {
-            // for non web intents, we should not resolve externally if an app already exists to
-            // handle it or if the caller didn't explicitly request it.
-            if ((resolvedActivities != null && resolvedActivities.size() != 0)
-                    || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
-                return false;
-            }
-        } else if (intent.getData() == null) {
+        final boolean isWebUri = hasWebURI(intent);
+        if (!isWebUri || intent.getData().getHost() == null) {
             return false;
         }
         // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
@@ -6386,7 +6370,7 @@
                 if (matches.get(i).getTargetUserId() == targetUserId) return true;
             }
         }
-        if (intent.hasWebURI()) {
+        if (hasWebURI(intent)) {
             // cross-profile app linking works only towards the parent.
             final int callingUid = Binder.getCallingUid();
             final UserInfo parent = getProfileParent(sourceUserId);
@@ -6561,7 +6545,7 @@
                         sortResult = true;
                     }
                 }
-                if (intent.hasWebURI()) {
+                if (hasWebURI(intent)) {
                     CrossProfileDomainInfo xpDomainInfo = null;
                     final UserInfo parent = getProfileParent(userId);
                     if (parent != null) {
@@ -6647,6 +6631,7 @@
                 if (ps.getInstantApp(userId)) {
                     final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
                     final int status = (int)(packedStatus >> 32);
+                    final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
                     if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
                         // there's a local instant application installed, but, the user has
                         // chosen to never use it; skip resolution and don't acknowledge
@@ -6678,8 +6663,9 @@
                         null /*responseObj*/, intent /*origIntent*/, resolvedType,
                         null /*callingPackage*/, userId, null /*verificationBundle*/,
                         resolveForStart);
-                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
-                        mInstantAppResolverConnection, requestObject);
+                auxiliaryResponse =
+                        InstantAppResolver.doInstantAppResolutionPhaseOne(
+                                mContext, mInstantAppResolverConnection, requestObject);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             } else {
                 // we have an instant application locally, but, we can't admit that since
@@ -6688,40 +6674,35 @@
                 // instant application available externally. when it comes time to start
                 // the instant application, we'll do the right thing.
                 final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
-                auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
-                                        ai.packageName, ai.versionCode, null /* splitName */);
+                auxiliaryResponse = new AuxiliaryResolveInfo(
+                        ai.packageName, null /*splitName*/, null /*failureActivity*/,
+                        ai.versionCode, null /*failureIntent*/);
             }
         }
-        if (intent.isBrowsableWebIntent() && auxiliaryResponse == null) {
-            return result;
+        if (auxiliaryResponse != null) {
+            if (DEBUG_EPHEMERAL) {
+                Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+            }
+            final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
+            final PackageSetting ps =
+                    mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
+            if (ps != null) {
+                ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+                        mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
+                ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token;
+                ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
+                // make sure this resolver is the default
+                ephemeralInstaller.isDefault = true;
+                ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+                        | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+                // add a non-generic filter
+                ephemeralInstaller.filter = new IntentFilter(intent.getAction());
+                ephemeralInstaller.filter.addDataPath(
+                        intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+                ephemeralInstaller.isInstantAppAvailable = true;
+                result.add(ephemeralInstaller);
+            }
         }
-        final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
-        if (ps == null) {
-            return result;
-        }
-        final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
-        ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
-                mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
-        ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
-                | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
-        // add a non-generic filter
-        ephemeralInstaller.filter = new IntentFilter();
-        if (intent.getAction() != null) {
-            ephemeralInstaller.filter.addAction(intent.getAction());
-        }
-        if (intent.getData() != null && intent.getData().getPath() != null) {
-            ephemeralInstaller.filter.addDataPath(
-                    intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
-        }
-        ephemeralInstaller.isInstantAppAvailable = true;
-        // make sure this resolver is the default
-        ephemeralInstaller.isDefault = true;
-        ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
-        if (DEBUG_EPHEMERAL) {
-            Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
-        }
-
-        result.add(ephemeralInstaller);
         return result;
     }
 
@@ -6836,11 +6817,10 @@
             final ResolveInfo info = resolveInfos.get(i);
             // allow activities that are defined in the provided package
             if (allowDynamicSplits
-                    && info.activityInfo != null
                     && info.activityInfo.splitName != null
                     && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
                             info.activityInfo.splitName)) {
-                if (mInstantAppInstallerActivity == null) {
+                if (mInstantAppInstallerInfo == null) {
                     if (DEBUG_INSTALL) {
                         Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
                     }
@@ -6852,15 +6832,14 @@
                 if (DEBUG_INSTALL) {
                     Slog.v(TAG, "Adding installer to the ResolveInfo list");
                 }
-                final ResolveInfo installerInfo = new ResolveInfo(
-                        mInstantAppInstallerInfo);
+                final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
                 final ComponentName installFailureActivity = findInstallFailureActivity(
                         info.activityInfo.packageName,  filterCallingUid, userId);
                 installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+                        info.activityInfo.packageName, info.activityInfo.splitName,
                         installFailureActivity,
-                        info.activityInfo.packageName,
                         info.activityInfo.applicationInfo.versionCode,
-                        info.activityInfo.splitName);
+                        null /*failureIntent*/);
                 installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
                         | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
                 // add a non-generic filter
@@ -6877,7 +6856,6 @@
                 installerInfo.priority = info.priority;
                 installerInfo.preferredOrder = info.preferredOrder;
                 installerInfo.isDefault = info.isDefault;
-                installerInfo.isInstantAppAvailable = true;
                 resolveInfos.set(i, installerInfo);
                 continue;
             }
@@ -6935,6 +6913,17 @@
         return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
     }
 
+    private static boolean hasWebURI(Intent intent) {
+        if (intent.getData() == null) {
+            return false;
+        }
+        final String scheme = intent.getScheme();
+        if (TextUtils.isEmpty(scheme)) {
+            return false;
+        }
+        return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
+    }
+
     private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
             int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
             int userId) {
@@ -7607,13 +7596,11 @@
                     if (DEBUG_EPHEMERAL) {
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
-                    final ResolveInfo installerInfo = new ResolveInfo(
-                            mInstantAppInstallerInfo);
+                    final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
-                            null /* installFailureActivity */,
-                            info.serviceInfo.packageName,
-                            info.serviceInfo.applicationInfo.versionCode,
-                            info.serviceInfo.splitName);
+                            info.serviceInfo.packageName, info.serviceInfo.splitName,
+                            null /*failureActivity*/, info.serviceInfo.applicationInfo.versionCode,
+                            null /*failureIntent*/);
                     // make sure this resolver is the default
                     installerInfo.isDefault = true;
                     installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -7729,13 +7716,11 @@
                     if (DEBUG_EPHEMERAL) {
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
-                    final ResolveInfo installerInfo = new ResolveInfo(
-                            mInstantAppInstallerInfo);
+                    final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
-                            null /*failureActivity*/,
-                            info.providerInfo.packageName,
-                            info.providerInfo.applicationInfo.versionCode,
-                            info.providerInfo.splitName);
+                            info.providerInfo.packageName, info.providerInfo.splitName,
+                            null /*failureActivity*/, info.providerInfo.applicationInfo.versionCode,
+                            null /*failureIntent*/);
                     // make sure this resolver is the default
                     installerInfo.isDefault = true;
                     installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -8367,13 +8352,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 +8729,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 +9711,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!");
+                        }
                     }
                 }
 
@@ -10169,6 +10172,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 +10207,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!");
             }
         }
 
@@ -11775,7 +11794,7 @@
         mInstantAppInstallerActivity.exported = true;
         mInstantAppInstallerActivity.enabled = true;
         mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity;
-        mInstantAppInstallerInfo.priority = 1;
+        mInstantAppInstallerInfo.priority = 0;
         mInstantAppInstallerInfo.preferredOrder = 1;
         mInstantAppInstallerInfo.isDefault = true;
         mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -13225,8 +13244,7 @@
     }
 
     static final class EphemeralIntentResolver
-            extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
-            AuxiliaryResolveInfo.AuxiliaryFilter> {
+            extends IntentResolver<AuxiliaryResolveInfo, AuxiliaryResolveInfo> {
         /**
          * The result that has the highest defined order. Ordering applies on a
          * per-package basis. Mapping is from package name to Pair of order and
@@ -13241,19 +13259,18 @@
         final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult = new ArrayMap<>();
 
         @Override
-        protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) {
-            return new AuxiliaryResolveInfo.AuxiliaryFilter[size];
+        protected AuxiliaryResolveInfo[] newArray(int size) {
+            return new AuxiliaryResolveInfo[size];
         }
 
         @Override
-        protected boolean isPackageForFilter(String packageName,
-                AuxiliaryResolveInfo.AuxiliaryFilter responseObj) {
+        protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) {
             return true;
         }
 
         @Override
-        protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(
-                AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) {
+        protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match,
+                int userId) {
             if (!sUserManager.exists(userId)) {
                 return null;
             }
@@ -13274,7 +13291,7 @@
         }
 
         @Override
-        protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) {
+        protected void filterResults(List<AuxiliaryResolveInfo> results) {
             // only do work if ordering is enabled [most of the time it won't be]
             if (mOrderResult.size() == 0) {
                 return;
@@ -16061,10 +16078,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 +17075,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..c02331d 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) {
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 2552643..fe5b3d4 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -61,10 +61,8 @@
     /** Whether or not the policy files have been read */
     private static boolean sPolicyRead;
 
-    /** Path to MAC permissions on system image */
-    private static final File[] MAC_PERMISSIONS =
-    { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"),
-      new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") };
+    /** Required MAC permissions files */
+    private static List<File> sMacPermissions = new ArrayList<>();
 
     // Append privapp to existing seinfo label
     private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -75,13 +73,40 @@
     // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
     private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
 
+    // Only initialize sMacPermissions once.
+    static {
+        // Platform mac permissions.
+        sMacPermissions.add(new File(
+            Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
+
+        // Vendor mac permissions.
+        // The filename has been renamed from nonplat_mac_permissions to
+        // vendor_mac_permissions. Either of them should exist.
+        final File vendorMacPermission = new File(
+            Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
+        if (vendorMacPermission.exists()) {
+            sMacPermissions.add(vendorMacPermission);
+        } else {
+            // For backward compatibility.
+            sMacPermissions.add(new File(Environment.getVendorDirectory(),
+                                         "/etc/selinux/nonplat_mac_permissions.xml"));
+        }
+
+        // ODM mac permissions (optional).
+        final File odmMacPermission = new File(
+            Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml");
+        if (odmMacPermission.exists()) {
+            sMacPermissions.add(odmMacPermission);
+        }
+    }
+
     /**
      * Load the mac_permissions.xml file containing all seinfo assignments used to
-     * label apps. The loaded mac_permissions.xml file is determined by the
-     * MAC_PERMISSIONS class variable which is set at class load time which itself
-     * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on
+     * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and
+     * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively.
+     * odm_mac_permissions.xml on /odm partition is optional. For further guidance on
      * the proper structure of a mac_permissions.xml file consult the source code
-     * located at system/sepolicy/mac_permissions.xml.
+     * located at system/sepolicy/private/mac_permissions.xml.
      *
      * @return boolean indicating if policy was correctly loaded. A value of false
      *         typically indicates a structural problem with the xml or incorrectly
@@ -100,10 +125,13 @@
 
         FileReader policyFile = null;
         XmlPullParser parser = Xml.newPullParser();
-        for (int i = 0; i < MAC_PERMISSIONS.length; i++) {
+
+        final int count = sMacPermissions.size();
+        for (int i = 0; i < count; ++i) {
+            final File macPermission = sMacPermissions.get(i);
             try {
-                policyFile = new FileReader(MAC_PERMISSIONS[i]);
-                Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]);
+                policyFile = new FileReader(macPermission);
+                Slog.d(TAG, "Using policy file " + macPermission);
 
                 parser.setInput(policyFile);
                 parser.nextTag();
@@ -127,13 +155,13 @@
                 StringBuilder sb = new StringBuilder("Exception @");
                 sb.append(parser.getPositionDescription());
                 sb.append(" while parsing ");
-                sb.append(MAC_PERMISSIONS[i]);
+                sb.append(macPermission);
                 sb.append(":");
                 sb.append(ex);
                 Slog.w(TAG, sb.toString());
                 return false;
             } catch (IOException ioe) {
-                Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe);
+                Slog.w(TAG, "Exception parsing " + macPermission, ioe);
                 return false;
             } finally {
                 IoUtils.closeQuietly(policyFile);
@@ -454,7 +482,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/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/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index efcadad..941cd44 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -19,8 +19,6 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.security.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
@@ -53,16 +51,11 @@
     private final LockPatternUtils mLockPatternUtils;
     private final StateCallback mCallback;
 
-    IKeystoreService mKeystoreService;
-
     public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) {
         mLockPatternUtils = new LockPatternUtils(context);
         mCurrentUserId = ActivityManager.getCurrentUser();
         mCallback = callback;
 
-        mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
-                .getService("android.security.keystore"));
-
         try {
             service.addStateMonitorCallback(this);
         } catch (RemoteException e) {
@@ -93,12 +86,6 @@
     @Override // Binder interface
     public void onShowingStateChanged(boolean showing) {
         mIsShowing = showing;
-
-        if (showing) try {
-            mKeystoreService.lock(mCurrentUserId); // as long as this doesn't recur...
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Error locking keystore", e);
-        }
     }
 
     @Override // Binder interface
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..9466350 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;
@@ -116,9 +117,16 @@
         }
     }
 
+    @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 +144,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;
     }
@@ -208,10 +220,12 @@
         return getStat(statesToIndex(batterySaverState, interactiveState, dozeState));
     }
 
+    @VisibleForTesting
     long injectCurrentTime() {
         return SystemClock.elapsedRealtime();
     }
 
+    @VisibleForTesting
     int injectBatteryLevel() {
         final BatteryManagerInternal bmi = getBatteryManagerInternal();
         if (bmi == null) {
@@ -227,15 +241,9 @@
      */
     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,23 +252,30 @@
      */
     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();
+
+        endLastStateLocked(now, batteryLevel);
+        startNewStateLocked(newState, now, batteryLevel);
+        mMetricsLoggerHelper.transitionState(newState, now, batteryLevel);
+    }
+
+    private void endLastStateLocked(long now, int batteryLevel) {
         if (mCurrentState < 0) {
             return;
         }
         final Stat stat = getStat(mCurrentState);
 
-        stat.endBatteryLevel = injectBatteryLevel();
-        stat.endTime = injectCurrentTime();
+        stat.endBatteryLevel = batteryLevel;
+        stat.endTime = now;
 
         final long deltaTime = stat.endTime - stat.startTime;
         final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
@@ -283,9 +298,10 @@
                 deltaDrain,
                 stat.totalTimeMillis,
                 stat.totalBatteryDrain);
+
     }
 
-    private void startNewStateLocked(int newState) {
+    private void startNewStateLocked(int newState, long now, int batteryLevel) {
         if (DEBUG) {
             Slog.d(TAG, "New state: " + stateToString(newState));
         }
@@ -296,8 +312,8 @@
         }
 
         final Stat stat = getStat(mCurrentState);
-        stat.startBatteryLevel = injectBatteryLevel();
-        stat.startTime = injectCurrentTime();
+        stat.startBatteryLevel = batteryLevel;
+        stat.startTime = now;
         stat.endTime = 0;
     }
 
@@ -349,5 +365,50 @@
                 onStat.totalBatteryDrain / 1000,
                 onStat.drainPerHour() / 1000.0));
     }
-}
 
+    @VisibleForTesting
+    class MetricsLoggerHelper {
+        private int mLastState = STATE_NOT_INITIALIZED;
+        private long mStartTime;
+        private int mStartBatteryLevel;
+
+        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) {
+            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;
+
+                    report(mLastState, deltaTime, deltaBattery);
+                }
+                mStartTime = now;
+                mStartBatteryLevel = batteryLevel;
+            }
+            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) {
+            final String suffix = getCounterSuffix(state);
+            mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+            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..591e809
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -0,0 +1,131 @@
+/*
+ * 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 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..5db0fc0 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;
@@ -32,20 +34,24 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 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 +61,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 +91,8 @@
     private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
     private final Handler mHandler;
     private final ContentObserver mObserver;
+    private final AtomicFile mSliceAccessFile;
+    private final SliceFullAccessList mAccessList;
 
     public SliceManagerService(Context context) {
         this(context, createHandler().getLooper());
@@ -99,6 +117,21 @@
                 }
             }
         };
+        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());
+                mAccessList.readXml(parser);
+            } catch (IOException | XmlPullParserException e) {
+                Slog.d(TAG, "Can't read slice access file", e);
+            }
+        }
     }
 
     ///  ----- Lifecycle stuff -----
@@ -120,8 +153,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 +164,6 @@
             throws RemoteException {
         verifyCaller(pkg);
         uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
-        enforceAccess(pkg, uri);
         if (getPinnedSlice(uri).removeSliceListener(listener)) {
             removePinnedSlice(uri);
         }
@@ -169,14 +203,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,16 +223,23 @@
         getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
                 "Slice granting requires MANAGE_SLICE_PERMISSIONS");
         if (allSlices) {
-            // TODO: Manage full access grants.
+            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);
             }
         }
     }
@@ -249,17 +290,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 +305,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 +327,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 +444,7 @@
     }
 
     private boolean isGrantedFullAccess(String pkg, int userId) {
-        // TODO: This will be user granted access, if we allow this through a prompt.
-        return false;
+        return mAccessList.hasFullAccess(pkg, userId);
     }
 
     private static ServiceThread createHandler() {
@@ -406,6 +454,32 @@
         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());
+                    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);
+                }
+            }
+        }
+    };
+
     public static class Lifecycle extends SystemService {
         private SliceManagerService mService;
 
@@ -440,10 +514,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 +531,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/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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 7ae1f24..80798bf 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -568,6 +568,14 @@
                 : null;
         final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
+
+        // Ensure an old dim that was shown for the docked stack divider is removed so we don't end
+        // up with dim layers that can no longer be removed.
+        if (mDimmedStack != null && mDimmedStack != stack) {
+            mDimmedStack.stopDimming();
+            mDimmedStack = null;
+        }
+
         if (visibleAndValid) {
             mDimmedStack = stack;
             stack.dim(alpha);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index deed7f1..68c8995 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -903,10 +903,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 +935,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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index cf54b67..ae5341b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -736,15 +736,11 @@
     }
 
     private void updateSurfaceBounds() {
-        updateSurfaceBounds(getPendingTransaction());
+        updateSurfaceSize(getPendingTransaction());
+        updateSurfacePosition();
         scheduleAnimation();
     }
 
-    void updateSurfaceBounds(SurfaceControl.Transaction transaction) {
-        updateSurfaceSize(transaction);
-        updateSurfacePosition(transaction);
-    }
-
     private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
         if (mSurfaceControl == null) {
             return;
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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 55c982c..53a8d82 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -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;
         }
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/job/JobSetTest.java b/services/tests/servicestests/src/com/android/server/job/JobSetTest.java
new file mode 100644
index 0000000..83bd9fc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/JobSetTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.job;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.Build;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.LocalServices;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class JobSetTest {
+    private static final String TAG = JobSetTest.class.getSimpleName();
+    private static final int SECONDARY_USER_ID_1 = 12;
+    private static final int SECONDARY_USER_ID_2 = 13;
+
+    private Context mContext;
+    private ComponentName mComponent;
+    private JobStore.JobSet mJobSet;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mComponent = new ComponentName(mContext, JobStoreTest.class);
+        mJobSet = new JobStore.JobSet();
+        final PackageManagerInternal pm = mock(PackageManagerInternal.class);
+        when(pm.getPackageTargetSdkVersion(anyString()))
+                .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        LocalServices.addService(PackageManagerInternal.class, pm);
+        assumeFalse("Test cannot run in user " + mContext.getUserId(),
+                mContext.getUserId() == SECONDARY_USER_ID_1
+                        || mContext.getUserId() == SECONDARY_USER_ID_2);
+    }
+
+    private JobStatus getJobStatusWithCallinUid(int jobId, int callingUid) {
+        final JobInfo jobInfo = new JobInfo.Builder(jobId, mComponent)
+                .setPeriodic(10)
+                .setRequiresCharging(true)
+                .build();
+        return JobStatus.createFromJobInfo(jobInfo, callingUid, mContext.getPackageName(),
+                mContext.getUserId(), "Test");
+    }
+
+    @Test
+    public void testBothMapsHaveSameJobs() {
+        final int callingUid1 = UserHandle.getUid(SECONDARY_USER_ID_1, 1);
+        final int callingUid2 = UserHandle.getUid(SECONDARY_USER_ID_2, 1);
+        final JobStatus testJob1 = getJobStatusWithCallinUid(1, callingUid1);
+        final JobStatus testJob2 = getJobStatusWithCallinUid(2, callingUid2);
+        mJobSet.add(testJob1);
+        mJobSet.add(testJob2);
+        for (int i = 11; i <= 20; i++) {
+            mJobSet.add(getJobStatusWithCallinUid(i, (i%2 == 0) ? callingUid2 : callingUid1));
+        }
+        assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs);
+        mJobSet.remove(testJob1);
+        mJobSet.remove(testJob2);
+        assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs);
+        mJobSet.removeJobsOfNonUsers(new int[] {mContext.getUserId(), SECONDARY_USER_ID_1});
+        assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs);
+        mJobSet.removeJobsOfNonUsers(new int[] {mContext.getUserId()});
+        assertTrue("mJobs should be empty", mJobSet.mJobs.size() == 0);
+        assertTrue("mJobsPerSourceUid should be empty", mJobSet.mJobsPerSourceUid.size() == 0);
+    }
+
+    private static void assertHaveSameJobs(SparseArray<ArraySet<JobStatus>> map1,
+            SparseArray<ArraySet<JobStatus>> map2) {
+        final ArraySet<JobStatus> set1 = new ArraySet<>();
+        final ArraySet<JobStatus> set2 = new ArraySet<>();
+        int size1 = 0;
+        for (int i = 0; i < map1.size(); i++) {
+            final ArraySet<JobStatus> jobs = map1.valueAt(i);
+            if (jobs == null) return;
+            size1 += jobs.size();
+            set1.addAll(jobs);
+        }
+        for (int i = 0; i < map2.size(); i++) {
+            final ArraySet<JobStatus> jobs = map2.valueAt(i);
+            if (jobs == null) return;
+            size1 -= jobs.size();
+            set2.addAll(jobs);
+        }
+        if (size1 != 0 || !set1.equals(set2)) {
+            dump("map1", map1);
+            dump("map2", map2);
+            fail("Both maps have different sets of jobs");
+        }
+    }
+
+    private static void dump(String prefix, SparseArray<ArraySet<JobStatus>> jobMap) {
+        final StringBuilder str = new StringBuilder();
+        for (int i = 0; i < jobMap.size(); i++) {
+            final ArraySet<JobStatus> jobs = jobMap.valueAt(i);
+            if (jobs == null) return;
+            str.append("[Key: " + jobMap.keyAt(i) + ", Value: {");
+            for (int j = 0; j < jobs.size(); j++) {
+                final JobStatus job = jobs.valueAt(j);
+                str.append("(s=" + job.getSourceUid() + ", c=" + job.getUid() + "), ");
+            }
+            str.append("}], ");
+        }
+        Log.d(TAG, prefix + ": " + str.toString());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+    }
+}
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/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index f788cac..c3714c8 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() {
@@ -60,8 +72,8 @@
             mTime += 60_000 * minutes;
         }
 
-        void drainBattery(int percent) {
-            mBatteryLevel -= percent;
+        void drainBattery(int value) {
+            mBatteryLevel -= value;
             if (mBatteryLevel < 0) {
                 mBatteryLevel = 0;
             }
@@ -81,6 +93,8 @@
         }
     }
 
+    public MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
+
     @Test
     public void testAll() {
         final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
@@ -202,4 +216,95 @@
                 "BS=1,I=1,D=2:{0m,0,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_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_TIME_SECONDS_PREFIX + "00", 60 * 3);
+
+        target.advanceClock(10);
+        target.drainBattery(10_000);
+
+        reset(mMetricsLogger);
+        target.startCharging();
+
+        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+        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_TIME_SECONDS_PREFIX + "10", 60);
+    }
 }
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..7c14d08
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91d86c6..a34e9f9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -27,8 +27,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.carrier.CarrierService;
+import android.telephony.ims.ImsReasonInfo;
 
-import com.android.ims.ImsReasonInfo;
 import com.android.internal.telephony.ICarrierConfigLoader;
 
 /**
@@ -1390,7 +1390,7 @@
             "allow_video_calling_fallback_bool";
 
     /**
-     * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+     * Defines operator-specific {@link ImsReasonInfo} mappings.
      *
      * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
      * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
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/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index a6dbc06..577ea7d 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -18,8 +18,8 @@
 
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
+import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.app.PendingIntent;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.text.TextUtils;
@@ -544,8 +544,16 @@
 
     /**
      * Returns the originating address (sender) of this SMS message in String
-     * form or null if unavailable
+     * form or null if unavailable.
+     *
+     * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP
+     * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
+     * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
+     * should be careful to avoid assumptions about the returned content.
+     *
+     * @return a String representation of the address; null if unavailable.
      */
+    @Nullable
     public String getOriginatingAddress() {
         return mWrappedSmsMessage.getOriginatingAddress();
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0a6d960..5c290da 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -48,12 +48,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.Log;
 
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
@@ -4462,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) {
@@ -4951,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;
@@ -5094,57 +5073,60 @@
         }
     }
 
-    /** @hide */
-    @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Feature {}
-
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
-     * @param callback Listener that will send updates to ImsManager when there are updates to
-     * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
-     * it is unavailable.
+     * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+     * status updates, if not already enabled.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsMMTelFeatureAndListen(int slotIndex,
-            IImsServiceFeatureCallback callback) {
+    public void enableIms(int slotId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getMMTelFeatureAndListen(slotIndex, callback);
+                telephony.enableIms(slotId);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "enableIms, RemoteException: "
                     + e.getMessage());
         }
-        return null;
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature for emergency calling or {@link null} if the service is not available. If an
-     * MMTelFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature
+     * status updates to disabled.
+     * @hide
+     */
+    public void disableIms(int slotId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.disableIms(slotId);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "disableIms, RemoteException: "
+                    + e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
+     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
+     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
      * @param callback Listener that will send updates to ImsManager when there are updates to
      * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
+     * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
      * it is unavailable.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsEmergencyMMTelFeatureAndListen(int slotIndex,
+    public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
             IImsServiceFeatureCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getEmergencyMMTelFeatureAndListen(slotIndex, callback);
+                return telephony.getMmTelFeatureAndListen(slotIndex, callback);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsEmergencyMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
                     + e.getMessage());
         }
         return null;
@@ -5196,6 +5178,25 @@
     }
 
     /**
+     * @return the {@IImsConfig} interface that corresponds with the slot index and feature.
+     * @param slotIndex The SIM slot corresponding to the ImsService ImsConfig is active for.
+     * @param feature An integer indicating the feature that we wish to get the ImsConfig for.
+     * Corresponds to features defined in ImsFeature.
+     * @hide
+     */
+    public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getImsConfig(slotIndex, feature);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
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/ims/ImsCallForwardInfo.aidl b/telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsCallForwardInfo.aidl
rename to telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
index a7c3f9a..b322b39 100644
--- a/telephony/java/com/android/ims/ImsCallForwardInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsCallForwardInfo;
diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
similarity index 77%
rename from telephony/java/com/android/ims/ImsCallForwardInfo.java
rename to telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index eeee0fc..6d72181 100644
--- a/telephony/java/com/android/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -24,23 +25,32 @@
  *
  * @hide
  */
-public class ImsCallForwardInfo implements Parcelable {
+@SystemApi
+public final class ImsCallForwardInfo implements Parcelable {
     // Refer to ImsUtInterface#CDIV_CF_XXX
+    /** @hide */
     public int mCondition;
     // 0: disabled, 1: enabled
+    /** @hide */
     public int mStatus;
     // 0x91: International, 0x81: Unknown
+    /** @hide */
     public int mToA;
     // Service class
+    /** @hide */
     public int mServiceClass;
     // Number (it will not include the "sip" or "tel" URI scheme)
+    /** @hide */
     public String mNumber;
     // No reply timer for CF
+    /** @hide */
     public int mTimeSeconds;
 
+    /** @hide */
     public ImsCallForwardInfo() {
     }
 
+    /** @hide */
     public ImsCallForwardInfo(Parcel in) {
         readFromParcel(in);
     }
@@ -91,4 +101,28 @@
             return new ImsCallForwardInfo[size];
         }
     };
+
+    public int getCondition() {
+        return mCondition;
+    }
+
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public int getToA() {
+        return mToA;
+    }
+
+    public int getServiceClass() {
+        return mServiceClass;
+    }
+
+    public String getNumber() {
+        return mNumber;
+    }
+
+    public int getTimeSeconds() {
+        return mTimeSeconds;
+    }
 }
diff --git a/telephony/java/com/android/ims/ImsCallProfile.aidl b/telephony/java/android/telephony/ims/ImsCallProfile.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsCallProfile.aidl
rename to telephony/java/android/telephony/ims/ImsCallProfile.aidl
index a356d13..e24e145 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.aidl
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsCallProfile;
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
similarity index 89%
rename from telephony/java/com/android/ims/ImsCallProfile.java
rename to telephony/java/android/telephony/ims/ImsCallProfile.java
index 693aaff..27e5f94 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,7 +33,8 @@
  *
  * @hide
  */
-public class ImsCallProfile implements Parcelable {
+@SystemApi
+public final class ImsCallProfile implements Parcelable {
     private static final String TAG = "ImsCallProfile";
 
     /**
@@ -110,52 +112,92 @@
      *      the video during voice call.
      *  conference_avail : Indicates if the session can be extended to the conference.
      */
+    /**
+     * @hide
+     */
     public static final String EXTRA_CONFERENCE = "conference";
+    /**
+     * @hide
+     */
     public static final String EXTRA_E_CALL = "e_call";
+    /**
+     * @hide
+     */
     public static final String EXTRA_VMS = "vms";
+    /**
+     * @hide
+     */
     public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+    /**
+     * @hide
+     */
     public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
 
     // Extra string for internal use only. OEMs should not use
     // this for packing extras.
+    /**
+     * @hide
+     */
     public static final String EXTRA_OEM_EXTRAS = "OemCallExtras";
 
     /**
-     * Integer extra properties
-     *  oir : Rule for originating identity (number) presentation, MO/MT.
+     * Rule for originating identity (number) presentation, MO/MT.
      *      {@link ImsCallProfile#OIR_DEFAULT}
      *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
      *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
-     *  cnap : Rule for calling name presentation
+     */
+    public static final String EXTRA_OIR = "oir";
+    /**
+     * Rule for calling name presentation
      *      {@link ImsCallProfile#OIR_DEFAULT}
      *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
      *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
-     *  dialstring : To identify the Ims call type, MO
-     *      {@link ImsCallProfile#DIALSTRING_NORMAL_CALL}
+     */
+    public static final String EXTRA_CNAP = "cnap";
+    /**
+     * To identify the Ims call type, MO
+     *      {@link ImsCallProfile#DIALSTRING_NORMAL}
      *      {@link ImsCallProfile#DIALSTRING_SS_CONF}
      *      {@link ImsCallProfile#DIALSTRING_USSD}
      */
-    public static final String EXTRA_OIR = "oir";
-    public static final String EXTRA_CNAP = "cnap";
     public static final String EXTRA_DIALSTRING = "dialstring";
 
     /**
      * Values for EXTRA_OIR / EXTRA_CNAP
      */
+    /**
+     * Default presentation for Originating Identity.
+     */
     public static final int OIR_DEFAULT = 0;    // "user subscription default value"
+    /**
+     * Restricted presentation for Originating Identity.
+     */
     public static final int OIR_PRESENTATION_RESTRICTED = 1;
+    /**
+     * Not restricted presentation for Originating Identity.
+     */
     public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+    /**
+     * Presentation unknown for Originating Identity.
+     */
     public static final int OIR_PRESENTATION_UNKNOWN = 3;
+    /**
+     * Payphone presentation for Originating Identity.
+     */
     public static final int OIR_PRESENTATION_PAYPHONE = 4;
 
+    //Values for EXTRA_DIALSTRING
     /**
-     * Values for EXTRA_DIALSTRING
+     * A default or normal normal call.
      */
-    // default (normal call)
     public static final int DIALSTRING_NORMAL = 0;
-    // Call for SIP-based user configuration
+    /**
+     * Call for SIP-based user configuration
+     */
     public static final int DIALSTRING_SS_CONF = 1;
-    // Call for USSD message
+    /**
+     * Call for USSD message
+     */
     public static final int DIALSTRING_USSD = 2;
 
     /**
@@ -215,8 +257,11 @@
      */
     public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
 
+    /** @hide */
     public int mServiceType;
+    /** @hide */
     public int mCallType;
+    /** @hide */
     public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
 
     /**
@@ -241,13 +286,17 @@
      * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
      * a {@link android.os.Binder}.
      */
+    /** @hide */
     public Bundle mCallExtras;
+    /** @hide */
     public ImsStreamMediaProfile mMediaProfile;
 
+    /** @hide */
     public ImsCallProfile(Parcel in) {
         readFromParcel(in);
     }
 
+    /** @hide */
     public ImsCallProfile() {
         mServiceType = SERVICE_TYPE_NORMAL;
         mCallType = CALL_TYPE_VOICE_N_VIDEO;
@@ -255,6 +304,7 @@
         mMediaProfile = new ImsStreamMediaProfile();
     }
 
+    /** @hide */
     public ImsCallProfile(int serviceType, int callType) {
         mServiceType = serviceType;
         mCallType = callType;
@@ -366,8 +416,28 @@
         }
     };
 
+    public int getServiceType() {
+        return mServiceType;
+    }
+
+    public int getCallType() {
+        return mCallType;
+    }
+
+    public int getRestrictCause() {
+        return mRestrictCause;
+    }
+
+    public Bundle getCallExtras() {
+        return mCallExtras;
+    }
+
+    public ImsStreamMediaProfile getMediaProfile() {
+        return mMediaProfile;
+    }
+
     /**
-     * Converts from the call types defined in {@link com.android.ims.ImsCallProfile} to the
+     * Converts from the call types defined in {@link ImsCallProfile} to the
      * video state values defined in {@link VideoProfile}.
      *
      * @param callProfile The call profile.
@@ -434,9 +504,9 @@
     }
 
     /**
-     * Translate presentation value to OIR value
-     * @param presentation
-     * @return OIR valuse
+     * Badly named old method, kept for compatibility.
+     * See {@link #presentationToOir(int)}.
+     * @hide
      */
     public static int presentationToOIR(int presentation) {
         switch (presentation) {
@@ -454,9 +524,19 @@
     }
 
     /**
+     * Translate presentation value to OIR value
+     * @param presentation
+     * @return OIR values
+     */
+    public static int presentationToOir(int presentation) {
+        return presentationToOIR(presentation);
+    }
+
+    /**
      * Translate OIR value to presentation value
      * @param oir value
      * @return presentation value
+     * @hide
      */
     public static int OIRToPresentation(int oir) {
         switch(oir) {
diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
similarity index 90%
rename from telephony/java/com/android/ims/internal/ImsCallSession.java
rename to telephony/java/android/telephony/ims/ImsCallSession.java
index 1736b80..c3d103f 100644
--- a/telephony/java/com/android/ims/internal/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.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.
@@ -11,23 +11,26 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims.internal;
+package android.telephony.ims;
 
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
 
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
-import android.telephony.ims.stub.ImsCallSessionListenerImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.util.Log;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
 
 /**
  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
@@ -39,7 +42,8 @@
     private static final String TAG = "ImsCallSession";
 
     /**
-     * Defines IMS call session state.
+     * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+     * This is kept around for capability reasons.
      */
     public static class State {
         public static final int IDLE = 0;
@@ -92,6 +96,7 @@
      * Listener for events relating to an IMS session, such as when a session is being
      * recieved ("on ringing") or a call is outgoing ("on calling").
      * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+     * @hide
      */
     public static class Listener {
         /**
@@ -449,6 +454,7 @@
     private boolean mClosed = false;
     private Listener mListener;
 
+    /** @hide */
     public ImsCallSession(IImsCallSession iSession) {
         miSession = iSession;
 
@@ -462,6 +468,7 @@
         }
     }
 
+    /** @hide */
     public ImsCallSession(IImsCallSession iSession, Listener listener) {
         this(iSession);
         setListener(listener);
@@ -470,15 +477,17 @@
     /**
      * Closes this object. This object is not usable after being closed.
      */
-    public synchronized void close() {
-        if (mClosed) {
-            return;
-        }
+    public void close() {
+        synchronized (this) {
+            if (mClosed) {
+                return;
+            }
 
-        try {
-            miSession.close();
-            mClosed = true;
-        } catch (RemoteException e) {
+            try {
+                miSession.close();
+                mClosed = true;
+            } catch (RemoteException e) {
+            }
         }
     }
 
@@ -554,6 +563,7 @@
      * Gets the video call provider for the session.
      *
      * @return The video call provider.
+     * @hide
      */
     public IImsVideoCallProvider getVideoCallProvider() {
         if (mClosed) {
@@ -659,6 +669,7 @@
      * override the previous listener.
      *
      * @param listener to listen to the session events of this object
+     * @hide
      */
     public void setListener(Listener listener) {
         mListener = listener;
@@ -987,7 +998,6 @@
      * Sends Rtt Message
      *
      * @param rttMessage rtt text to be sent
-     * @throws ImsException if call is absent
      */
     public void sendRttMessage(String rttMessage) {
         if (mClosed) {
@@ -1004,7 +1014,6 @@
      * Sends RTT Upgrade request
      *
      * @param to   : expected profile
-     * @throws CallStateException
      */
     public void sendRttModifyRequest(ImsCallProfile to) {
         if (mClosed) {
@@ -1021,7 +1030,6 @@
      * Sends RTT Upgrade response
      *
      * @param response : response for upgrade
-     * @throws CallStateException
      */
     public void sendRttModifyResponse(boolean response) {
         if (mClosed) {
@@ -1040,37 +1048,33 @@
      * the application is notified by having one of the methods called on
      * the {@link IImsCallSessionListener}.
      */
-    private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase {
+    private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
         /**
          * Notifies the result of the basic session operation (setup / terminate).
          */
         @Override
-        public void callSessionProgressing(IImsCallSession session,
-                ImsStreamMediaProfile profile) {
+        public void callSessionProgressing(ImsStreamMediaProfile profile) {
             if (mListener != null) {
                 mListener.callSessionProgressing(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionStarted(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionInitiated(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionStarted(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionStartFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
             }
         }
 
         @Override
-        public void callSessionTerminated(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionTerminated(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
             }
@@ -1080,48 +1084,42 @@
          * Notifies the result of the call hold/resume operation.
          */
         @Override
-        public void callSessionHeld(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionHeld(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionHeld(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionHoldFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
             }
         }
 
         @Override
-        public void callSessionHoldReceived(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionHoldReceived(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionHoldReceived(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionResumed(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionResumed(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionResumed(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionResumeFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
             }
         }
 
         @Override
-        public void callSessionResumeReceived(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionResumeReceived(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionResumeReceived(ImsCallSession.this, profile);
             }
@@ -1130,13 +1128,11 @@
         /**
          * Notifies the start of a call merge operation.
          *
-         * @param session The call session.
          * @param newSession The merged call session.
          * @param profile The call profile.
          */
         @Override
-        public void callSessionMergeStarted(IImsCallSession session,
-                IImsCallSession newSession, ImsCallProfile profile) {
+        public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
             // This callback can be used for future use to add additional
             // functionality that may be needed between conference start and complete
             Log.d(TAG, "callSessionMergeStarted");
@@ -1173,12 +1169,10 @@
         /**
          * Notifies of a failure to perform a call merge operation.
          *
-         * @param session The call session.
          * @param reasonInfo The merge failure reason.
          */
         @Override
-        public void callSessionMergeFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
             }
@@ -1188,24 +1182,21 @@
          * Notifies the result of call upgrade / downgrade or any other call updates.
          */
         @Override
-        public void callSessionUpdated(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionUpdated(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionUpdated(ImsCallSession.this, profile);
             }
         }
 
         @Override
-        public void callSessionUpdateFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
             }
         }
 
         @Override
-        public void callSessionUpdateReceived(IImsCallSession session,
-                ImsCallProfile profile) {
+        public void callSessionUpdateReceived(ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
             }
@@ -1215,8 +1206,8 @@
          * Notifies the result of conference extension.
          */
         @Override
-        public void callSessionConferenceExtended(IImsCallSession session,
-                IImsCallSession newSession, ImsCallProfile profile) {
+        public void callSessionConferenceExtended(IImsCallSession newSession,
+                ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionConferenceExtended(ImsCallSession.this,
                         new ImsCallSession(newSession), profile);
@@ -1224,16 +1215,15 @@
         }
 
         @Override
-        public void callSessionConferenceExtendFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
             }
         }
 
         @Override
-        public void callSessionConferenceExtendReceived(IImsCallSession session,
-                IImsCallSession newSession, ImsCallProfile profile) {
+        public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+                ImsCallProfile profile) {
             if (mListener != null) {
                 mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
                         new ImsCallSession(newSession), profile);
@@ -1245,15 +1235,14 @@
          * the conference session.
          */
         @Override
-        public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+        public void callSessionInviteParticipantsRequestDelivered() {
             if (mListener != null) {
                 mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
             }
         }
 
         @Override
-        public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
                         reasonInfo);
@@ -1261,15 +1250,14 @@
         }
 
         @Override
-        public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+        public void callSessionRemoveParticipantsRequestDelivered() {
             if (mListener != null) {
                 mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
             }
         }
 
         @Override
-        public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
-                ImsReasonInfo reasonInfo) {
+        public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
                         reasonInfo);
@@ -1280,8 +1268,7 @@
          * Notifies the changes of the conference info. in the conference session.
          */
         @Override
-        public void callSessionConferenceStateUpdated(IImsCallSession session,
-                ImsConferenceState state) {
+        public void callSessionConferenceStateUpdated(ImsConferenceState state) {
             if (mListener != null) {
                 mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
             }
@@ -1291,17 +1278,15 @@
          * Notifies the incoming USSD message.
          */
         @Override
-        public void callSessionUssdMessageReceived(IImsCallSession session,
-                int mode, String ussdMessage) {
+        public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
             if (mListener != null) {
                 mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
             }
         }
 
         /**
-         * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may
+         * Notifies of a case where a {@link ImsCallSession} may
          * potentially handover from one radio technology to another.
-         * @param session
          * @param srcAccessTech The source radio access technology; one of the access technology
          *                      constants defined in {@link android.telephony.ServiceState}.  For
          *                      example
@@ -1312,8 +1297,7 @@
          *                      {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
          */
         @Override
-        public void callSessionMayHandover(IImsCallSession session,
-                int srcAccessTech, int targetAccessTech) {
+        public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
             if (mListener != null) {
                 mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
                         targetAccessTech);
@@ -1324,9 +1308,8 @@
          * Notifies of handover information for this call
          */
         @Override
-        public void callSessionHandover(IImsCallSession session,
-                                 int srcAccessTech, int targetAccessTech,
-                                 ImsReasonInfo reasonInfo) {
+        public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
                         targetAccessTech, reasonInfo);
@@ -1337,9 +1320,8 @@
          * Notifies of handover failure info for this call
          */
         @Override
-        public void callSessionHandoverFailed(IImsCallSession session,
-                                       int srcAccessTech, int targetAccessTech,
-                                       ImsReasonInfo reasonInfo) {
+        public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
                         targetAccessTech, reasonInfo);
@@ -1350,8 +1332,7 @@
          * Notifies the TTY mode received from remote party.
          */
         @Override
-        public void callSessionTtyModeReceived(IImsCallSession session,
-                int mode) {
+        public void callSessionTtyModeReceived(int mode) {
             if (mListener != null) {
                 mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
             }
@@ -1360,21 +1341,17 @@
         /**
          * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
          *
-         * @param session The call session.
          * @param isMultiParty {@code true} if the session became multiparty, {@code false}
          *      otherwise.
          */
-        public void callSessionMultipartyStateChanged(IImsCallSession session,
-                boolean isMultiParty) {
-
+        public void callSessionMultipartyStateChanged(boolean isMultiParty) {
             if (mListener != null) {
                 mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
             }
         }
 
         @Override
-        public void callSessionSuppServiceReceived(IImsCallSession session,
-                ImsSuppServiceNotification suppServiceInfo ) {
+        public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
             if (mListener != null) {
                 mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
             }
@@ -1384,8 +1361,7 @@
          * Received RTT modify request from remote party
          */
         @Override
-        public void callSessionRttModifyRequestReceived(IImsCallSession session,
-                ImsCallProfile callProfile) {
+        public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
             if (mListener != null) {
                 mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
             }
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..a7f124a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,603 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing
+ * IMS call.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this implementation or you
+// will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+// TODO: APIs in here do not conform to API guidelines yet. This can be changed if
+// ImsCallSessionListenerConverter is also changed.
+@SystemApi
+public class ImsCallSessionListener {
+
+    private final IImsCallSessionListener mListener;
+
+    /** @hide */
+    public ImsCallSessionListener(IImsCallSessionListener l) {
+        mListener = l;
+    }
+
+    /**
+     * A request has been sent out to initiate a new IMS call session and a 1xx response has been
+     * received from the network.
+     */
+    public void callSessionProgressing(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionProgressing(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has been initiated.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiated(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     */
+    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatedFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has been terminated.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session termination.
+     */
+    public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionTerminated(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of holding the call. If it fails,
+     * {@link #callSessionHoldFailed(ImsReasonInfo)} should be called.
+     *
+     * If the IMS call session is resumed, call {@link #callSessionResumed(ImsCallProfile)}.
+     *
+     * @param profile The associated {@link ImsCallProfile} of the call session that has been put
+     * on hold.
+     */
+    public void callSessionHeld(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHeld(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has failed to be held.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session hold failure.
+     */
+    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHoldFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * This IMS Call session has been put on hold by the remote party.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionHoldReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHoldReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of resuming the call. If the process of resuming
+     * the call fails, call {@link #callSessionResumeFailed(ImsReasonInfo)}.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionResumed(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumed(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session resume has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the session resume
+     * failure.
+     */
+    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionResumeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The remote party has resumed this IMS call session.
+     *
+     * @param profile {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionResumeReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumeReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session merge has been started.  At this point, the {@code newSession}
+     * represents the IMS call session which represents the new merged conference and has been
+     * initiated to the IMS conference server.
+     *
+     * @param newSession the {@link ImsCallSessionImplBase} that represents the merged active & held
+     * sessions.
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionMergeStarted(ImsCallSessionImplBase newSession, ImsCallProfile profile)
+    {
+        try {
+            mListener.callSessionMergeStarted(newSession != null ?
+                            newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations.
+     * See {@link #callSessionMergeStarted(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)
+    {
+        try {
+            mListener.callSessionMergeStarted(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The session merge is successful and the merged {@link ImsCallSession} is active.
+     *
+     * @param newSession the new {@link ImsCallSessionImplBase}
+     *                  that represents the conference IMS call
+     * session.
+     */
+    public void callSessionMergeComplete(ImsCallSessionImplBase newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession != null ?
+                    newSession.getServiceImpl() : null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations of ImsService.
+     *
+     * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+     *
+     * @hide
+     */
+    public void callSessionMergeComplete(IImsCallSession newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session merge has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} contining the reason for the call merge failure.
+     */
+    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionMergeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile has been updated. Does not include holding or resuming a call.
+     *
+     * @param profile The {@link ImsCallProfile} associated with the updated IMS call session.
+     */
+    public void callSessionUpdated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdated(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile update has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing a reason for the session update failure.
+     */
+    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionUpdateFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile has received an update from the remote user.
+     *
+     * @param profile The new {@link ImsCallProfile} associated with the update.
+     */
+    public void callSessionUpdateReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdateReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Called when the session has been extended to a conference session.
+     *
+     * If the conference extension fails, call
+     * {@link #callSessionConferenceExtendFailed(ImsReasonInfo)}.
+     *
+     * @param newSession the session object that is extended to the conference from the active
+     * IMS Call session.
+     * @param profile The {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionConferenceExtended(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtended(
+                    newSession != null ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtended(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtended(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous conference extension has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the conference
+     * extension failure.
+     */
+    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionConferenceExtendFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * A conference extension has been received received from the remote party.
+     *
+     * @param newSession An {@link ImsCallSessionImplBase}
+     *                   representing the extended IMS call session.
+     * @param profile The {@link ImsCallProfile} associated with the new IMS call session.
+     */
+    public void callSessionConferenceExtendReceived(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtendReceived(newSession != null
+                    ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtendReceived(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtendReceived(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to invite participants to the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionInviteParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionInviteParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous request to invite participants to the conference (see
+     * {@link #callSessionInviteParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason forthe conference invitation
+     * failure.
+     */
+    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to remove participants from the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionRemoveParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionRemoveParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous request to remove participants from the conference (see
+     * {@link #callSessionRemoveParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason for the conference removal
+     * failure.
+     */
+    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's conference state has changed.
+     *
+     * @param state The new {@link ImsConferenceState} associated with the conference.
+     */
+    public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+        try {
+            mListener.callSessionConferenceStateUpdated(state);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has received a Ussd message.
+     *
+     * @param mode The mode of the USSD message, either
+     * {@link ImsCallSessionImplBase#USSD_MODE_NOTIFY} or
+     * {@link ImsCallSessionImplBase#USSD_MODE_REQUEST}.
+     * @param ussdMessage The USSD message.
+     */
+    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+    {
+        try {
+            mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An {@link ImsCallSession} may potentially handover from one radio
+     * technology to another.
+     *
+     * @param srcAccessTech The source radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @param targetAccessTech The target radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     */
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+    {
+        try {
+            mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's access technology has changed.
+     *
+     * @param srcAccessTech original access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param targetAccessTech new access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+     */
+    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's access technology change has failed..
+     *
+     * @param srcAccessTech original access technology
+     * @param targetAccessTech new access technology
+     * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     */
+    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The TTY mode has been changed by the remote party.
+     *
+     * @param mode one of the following: -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    public void callSessionTtyModeReceived(int mode) {
+        try {
+            mListener.callSessionTtyModeReceived(mode);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The multiparty state has been changed for this {@code ImsCallSession}.
+     *
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+        try {
+            mListener.callSessionMultipartyStateChanged(isMultiParty);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Supplementary service information has been received for the current IMS call session.
+     *
+     * @param suppSrvNotification The {@link ImsSuppServiceNotification} containing the change.
+     */
+    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+    {
+        try {
+            mListener.callSessionSuppServiceReceived(suppSrvNotification);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT modify request has been received from the remote party.
+     *
+     * @param callProfile An {@link ImsCallProfile} with the updated attributes
+     */
+    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+    {
+        try {
+            mListener.callSessionRttModifyRequestReceived(callProfile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT modify response has been received.
+     *
+     * @param status the received response for RTT modify request.
+     */
+    public void callSessionRttModifyResponseReceived(int status) {
+        try {
+            mListener.callSessionRttModifyResponseReceived(status);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT message has been received from the remote party.
+     *
+     * @param rttMessage The RTT message that has been received.
+     */
+    public void callSessionRttMessageReceived(String rttMessage) {
+        try {
+            mListener.callSessionRttMessageReceived(rttMessage);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
+
diff --git a/telephony/java/com/android/ims/ImsConferenceState.aidl b/telephony/java/android/telephony/ims/ImsConferenceState.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsConferenceState.aidl
rename to telephony/java/android/telephony/ims/ImsConferenceState.aidl
index 2fc029f..e2b371c 100644
--- a/telephony/java/com/android/ims/ImsConferenceState.aidl
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsConferenceState;
diff --git a/telephony/java/com/android/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
similarity index 95%
rename from telephony/java/com/android/ims/ImsConferenceState.java
rename to telephony/java/android/telephony/ims/ImsConferenceState.java
index 0afde88..66d2f8d 100644
--- a/telephony/java/com/android/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.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.
@@ -11,16 +11,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,7 +33,8 @@
  *
  * @hide
  */
-public class ImsConferenceState implements Parcelable {
+@SystemApi
+public final class ImsConferenceState implements Parcelable {
     /**
      * conference-info : user
      */
@@ -87,12 +89,13 @@
      */
     public static final String SIP_STATUS_CODE = "sipstatuscode";
 
-    public HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+    public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
 
+    /** @hide */
     public ImsConferenceState() {
     }
 
-    public ImsConferenceState(Parcel in) {
+    private ImsConferenceState(Parcel in) {
         readFromParcel(in);
     }
 
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.aidl b/telephony/java/android/telephony/ims/ImsExternalCallState.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsExternalCallState.aidl
rename to telephony/java/android/telephony/ims/ImsExternalCallState.aidl
index c208702..99d2935 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.aidl
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsExternalCallState;
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
similarity index 92%
rename from telephony/java/com/android/ims/ImsExternalCallState.java
rename to telephony/java/android/telephony/ims/ImsExternalCallState.java
index da26073..e82c115 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,7 +33,8 @@
  * Parcelable object to handle MultiEndpoint Dialog Information
  * @hide
  */
-public class ImsExternalCallState implements Parcelable {
+@SystemApi
+public final class ImsExternalCallState implements Parcelable {
 
     private static final String TAG = "ImsExternalCallState";
 
@@ -50,9 +52,11 @@
     private int mCallType;
     private boolean mIsHeld;
 
+    /** @hide */
     public ImsExternalCallState() {
     }
 
+    /** @hide */
     public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
             int callType, boolean isCallheld) {
         mCallId = callId;
@@ -64,6 +68,7 @@
         Rlog.d(TAG, "ImsExternalCallState = " + this);
     }
 
+    /** @hide */
     public ImsExternalCallState(Parcel in) {
         mCallId = in.readInt();
         ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.aidl b/telephony/java/android/telephony/ims/ImsReasonInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsReasonInfo.aidl
rename to telephony/java/android/telephony/ims/ImsReasonInfo.aidl
index 17e6d3a..604b323 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsReasonInfo;
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
similarity index 97%
rename from telephony/java/com/android/ims/ImsReasonInfo.java
rename to telephony/java/android/telephony/ims/ImsReasonInfo.java
index 83d9bd9..7b77491 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -24,7 +25,8 @@
  *
  * @hide
  */
-public class ImsReasonInfo implements Parcelable {
+@SystemApi
+public final class ImsReasonInfo implements Parcelable {
 
     /**
      * Specific code of each types
@@ -418,27 +420,36 @@
 
 
     // For main reason code
+    /** @hide */
     public int mCode;
     // For the extra code value; it depends on the code value.
+    /** @hide */
     public int mExtraCode;
     // For the additional message of the reason info.
+    /** @hide */
     public String mExtraMessage;
+
+    /** @hide */
     public ImsReasonInfo() {
         mCode = CODE_UNSPECIFIED;
         mExtraCode = CODE_UNSPECIFIED;
         mExtraMessage = null;
     }
 
-    public ImsReasonInfo(Parcel in) {
-        readFromParcel(in);
+    private ImsReasonInfo(Parcel in) {
+        mCode = in.readInt();
+        mExtraCode = in.readInt();
+        mExtraMessage = in.readString();
     }
 
+    /** @hide */
     public ImsReasonInfo(int code, int extraCode) {
         mCode = code;
         mExtraCode = extraCode;
         mExtraMessage = null;
     }
 
+    /** @hide */
     public ImsReasonInfo(int code, int extraCode, String extraMessage) {
         mCode = code;
         mExtraCode = extraCode;
@@ -487,12 +498,6 @@
         out.writeString(mExtraMessage);
     }
 
-    private void readFromParcel(Parcel in) {
-        mCode = in.readInt();
-        mExtraCode = in.readInt();
-        mExtraMessage = in.readString();
-    }
-
     public static final Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
         @Override
         public ImsReasonInfo createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index aaa0f08..2748cb5 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,25 +16,28 @@
 
 package android.telephony.ims;
 
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
 import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
 
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
@@ -48,9 +51,7 @@
  * ...
  * <service android:name=".EgImsService"
  *     android:permission="android.permission.BIND_IMS_SERVICE" >
- *     <!-- Apps must declare which features they support as metadata. The different categories are
- *     defined below. In this example, the RCS_FEATURE feature is supported. -->
- *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     ...
  *     <intent-filter>
  *         <action android:name="android.telephony.ims.ImsService" />
  *     </intent-filter>
@@ -64,13 +65,31 @@
  * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
  *
+ * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
+ * supports, dynamic or static definitions.
+ *
+ * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
+ * definition of the AndroidManifest.xml file as metadata:
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ *      defined below. In this example, the MMTEL_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
+ *
  * The features that are currently supported in an ImsService are:
  * - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
- * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
- *   available to place emergency calls at all times. This MUST be implemented by the default
- *   ImsService provided in the device overlay.
- *   @hide
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
+ *   declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
+ *   circuit switch for emergency calling.
+ *
+ * In the dynamic definition, the supported features are not specified in the service definition
+ * of the AndroidManifest. Instead, the framework binds to this service and calls
+ * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
+ * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
+ * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
+ * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
+ * framework of the changes.
+ *
+ * @hide
  */
 @SystemApi
 public class ImsService extends Service {
@@ -89,20 +108,36 @@
     // call ImsFeature#onFeatureRemoved.
     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
+    private IImsServiceControllerListener mListener;
+
+
+    /**
+     * Listener that notifies the framework of ImsService changes.
+     * @hide
+     */
+    public static class Listener extends IImsServiceControllerListener.Stub {
+        /**
+         * The IMS features that this ImsService supports has changed.
+         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+         *   that this ImsService supports. This may trigger the addition/removal of feature
+         *   in this service.
+         */
+        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+        }
+    }
+
     /**
      * @hide
      */
     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
-
         @Override
-        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
-                IImsFeatureStatusCallback c) {
-            return createEmergencyMMTelFeatureInternal(slotId, c);
+        public void setListener(IImsServiceControllerListener l) {
+            mListener = l;
         }
 
         @Override
-        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMMTelFeatureInternal(slotId, c);
+        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMmTelFeatureInternal(slotId, c);
         }
 
         @Override
@@ -111,16 +146,46 @@
         }
 
         @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
             ImsService.this.removeImsFeature(slotId, featureType, c);
         }
 
         @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return ImsService.this.querySupportedImsFeatures();
+        }
+
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+            ImsService.this.readyForFeatureCreation();
+        }
+
+        @Override
+        public void notifyImsFeatureReady(int slotId, int featureType) {
+            ImsService.this.notifyImsFeatureReady(slotId, featureType);
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId) {
+            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+            return c != null ? c.getIImsConfig() : null;
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) {
             ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
             return r != null ? r.getBinder() : null;
         }
+
+        @Override
+        public void enableIms(int slotId) {
+            ImsService.this.enableIms(slotId);
+        }
+
+        @Override
+        public void disableIms(int slotId) {
+            ImsService.this.disableIms(slotId);
+        }
     };
 
     /**
@@ -143,47 +208,35 @@
         return mFeaturesBySlot.get(slotId);
     }
 
-    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        MmTelFeature f = createMmTelFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
             return f.getBinder();
         } else {
-            return null;
-        }
-    }
-
-    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateMMTelImsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.MMTEL, c);
-            return f.getBinder();
-        } else {
+            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private IImsRcsFeature createRcsFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        RcsFeature f = onCreateRcsFeature(slotId);
+        RcsFeature f = createRcsFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.RCS, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
             return f.getBinder();
         } else {
+            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private void setupFeature(ImsFeature f, int slotId, int featureType,
             IImsFeatureStatusCallback c) {
-        f.setContext(this);
-        f.setSlotId(slotId);
         f.addImsFeatureStatusCallback(c);
+        f.initialize(this, slotId);
         addImsFeature(slotId, featureType, f);
-        // TODO: Remove once new onFeatureReady AIDL is merged in.
-        f.onFeatureReady();
     }
 
     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -221,38 +274,122 @@
         }
     }
 
+    private void notifyImsFeatureReady(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
+                        "slot " + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.onFeatureReady();
+        }
+    }
+
     /**
-     * @return An implementation of MMTelFeature that will be used by the system for MMTel
-     * functionality. Must be able to handle emergency calls at any time as well.
-     * @hide
+     * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
+     * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
+     * correspond to the {@link ImsFeature}s configured here.
+     *
+     * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
+     * {@link ImsFeature}s.
+     *
+     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
      */
-    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+    public ImsFeatureConfiguration querySupportedImsFeatures() {
+        // Return empty for base implementation
+        return new ImsFeatureConfiguration();
+    }
+
+    /**
+     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+     * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
+     * new ImsFeatures, depending on the configuration.
+     */
+    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+            throws RemoteException {
+        if (mListener == null) {
+            throw new IllegalStateException("Framework is not ready");
+        }
+        mListener.onUpdateSupportedImsFeatures(c);
+    }
+
+    /**
+     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+     * the ImsService has registered for with the framework, either in the manifest or via
+     * {@link #querySupportedImsFeatures()}.
+     *
+     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+     * feature initialization because the framework may bind to this service multiple times to
+     * query the ImsService's {@link ImsFeatureConfiguration} via
+     * {@link #querySupportedImsFeatures()}before creating features.
+     */
+    public void readyForFeatureCreation() {
+    }
+
+    /**
+     * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
+     * and perform all appropriate initialization to bring up all ImsFeatures.
+     */
+    public void enableIms(int slotId) {
+    }
+
+    /**
+     * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
+     * and set capability status to false for all ImsFeatures.
+     */
+    public void disableIms(int slotId) {
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified slot.
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the slot or null if the
+     * feature is not supported.
+     */
+    public MmTelFeature createMmTelFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of MMTelFeature that will be used by the system for MMTel
-     * functionality.
-     * @hide
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified slot.
+     *
+     * @param slotId The slot ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+     * is not supported.
      */
-    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+    public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of RcsFeature that will be used by the system for RCS.
-     * @hide
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+     * will be used by the platform to get/set specific IMS related configurations.
+     *
+     * @param slotId The slot that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified slot.
      */
-    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
-        return null;
+    public ImsConfigImplBase getConfig(int slotId) {
+        return new ImsConfigImplBase();
     }
 
     /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     *
      * @param slotId The slot that is associated with the IMS Registration.
      * @return the ImsRegistration implementation associated with the slot.
-     * @hide
      */
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
-}
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsSsData.aidl b/telephony/java/android/telephony/ims/ImsSsData.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSsData.aidl
rename to telephony/java/android/telephony/ims/ImsSsData.aidl
index 33f8306..eff3a6b 100644
--- a/telephony/java/com/android/ims/ImsSsData.aidl
+++ b/telephony/java/android/telephony/ims/ImsSsData.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsSsData;
diff --git a/telephony/java/com/android/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
similarity index 89%
rename from telephony/java/com/android/ims/ImsSsData.java
rename to telephony/java/android/telephony/ims/ImsSsData.java
index 7336c13..1ddf199 100644
--- a/telephony/java/com/android/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 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.
@@ -11,21 +11,21 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.ArrayList;
-
 /**
  * Provided STK Call Control Suplementary Service information
  *
  * {@hide}
  */
-public class ImsSsData implements Parcelable {
+@SystemApi
+public final class ImsSsData implements Parcelable {
 
     //ServiceType
     public static final int SS_CFU = 0;
@@ -68,30 +68,38 @@
     public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
 
     // Refer to ServiceType
+    /** @hide */
     public int serviceType;
     // Refere to SSRequestType
+    /** @hide */
     public int requestType;
     // Refer to TeleserviceType
+    /** @hide */
     public int teleserviceType;
     // Service Class
+    /** @hide */
     public int serviceClass;
     // Error information
+    /** @hide */
     public int result;
 
+    /** @hide */
     public int[] ssInfo; /* Valid for all supplementary services.
                              This field will be empty for RequestType SS_INTERROGATION
                              and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
                              SS_INCOMING_BARRING_ANONYMOUS.*/
 
+    /** @hide */
     public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services
                                             ServiceType SS_CF_* and RequestType SS_INTERROGATION */
 
+    /** @hide */
     public ImsSsInfo[] imsSsInfo;   /* Valid only for ServiceType SS_INCOMING_BARRING_DN and
                                         ServiceType SS_INCOMING_BARRING_ANONYMOUS */
 
     public ImsSsData() {}
 
-    public ImsSsData(Parcel in) {
+    private ImsSsData(Parcel in) {
         readFromParcel(in);
     }
 
@@ -133,20 +141,36 @@
         return 0;
     }
 
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
     public boolean isTypeCF() {
         return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
               serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
               serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
     }
 
+    public boolean isTypeCf() {
+        return isTypeCF();
+    }
+
     public boolean isTypeUnConditional() {
         return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
     }
 
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
     public boolean isTypeCW() {
         return (serviceType == SS_WAIT);
     }
 
+    public boolean isTypeCw() {
+        return isTypeCW();
+    }
+
     public boolean isTypeClip() {
         return (serviceType == SS_CLIP);
     }
diff --git a/telephony/java/com/android/ims/ImsSsInfo.aidl b/telephony/java/android/telephony/ims/ImsSsInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSsInfo.aidl
rename to telephony/java/android/telephony/ims/ImsSsInfo.aidl
index 0ac598b..66d4950 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsSsInfo;
diff --git a/telephony/java/com/android/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
similarity index 81%
rename from telephony/java/com/android/ims/ImsSsInfo.java
rename to telephony/java/android/telephony/ims/ImsSsInfo.java
index 7acc3bf..1d1292f 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -24,7 +25,8 @@
  *
  * @hide
  */
-public class ImsSsInfo implements Parcelable {
+@SystemApi
+public final class ImsSsInfo implements Parcelable {
     /**
      * For the status of service registration or activation/deactivation.
      */
@@ -33,13 +35,15 @@
     public static final int ENABLED = 1;
 
     // 0: disabled, 1: enabled
+    /** @hide */
     public int mStatus;
+    /** @hide */
     public String mIcbNum;
 
     public ImsSsInfo() {
     }
 
-    public ImsSsInfo(Parcel in) {
+    private ImsSsInfo(Parcel in) {
         readFromParcel(in);
     }
 
@@ -76,4 +80,12 @@
             return new ImsSsInfo[size];
         }
     };
+
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public String getIcbNum() {
+        return mIcbNum;
+    }
 }
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
rename to telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
index d648a35..ee321ae 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsStreamMediaProfile;
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
similarity index 88%
rename from telephony/java/com/android/ims/ImsStreamMediaProfile.java
rename to telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index cfe37b5..243352b 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.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.
@@ -11,11 +11,12 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,7 +26,8 @@
  *
  * @hide
  */
-public class ImsStreamMediaProfile implements Parcelable {
+@SystemApi
+public final class ImsStreamMediaProfile implements Parcelable {
     private static final String TAG = "ImsStreamMediaProfile";
 
     /**
@@ -79,18 +81,25 @@
     public static final int RTT_MODE_FULL = 1;
 
     // Audio related information
+    /** @hide */
     public int mAudioQuality;
+    /** @hide */
     public int mAudioDirection;
     // Video related information
+    /** @hide */
     public int mVideoQuality;
+    /** @hide */
     public int mVideoDirection;
     // Rtt related information
+    /** @hide */
     public int mRttMode;
 
+    /** @hide */
     public ImsStreamMediaProfile(Parcel in) {
         readFromParcel(in);
     }
 
+    /** @hide */
     public ImsStreamMediaProfile() {
         mAudioQuality = AUDIO_QUALITY_NONE;
         mAudioDirection = DIRECTION_SEND_RECEIVE;
@@ -99,6 +108,7 @@
         mRttMode = RTT_MODE_DISABLED;
     }
 
+    /** @hide */
     public ImsStreamMediaProfile(int audioQuality, int audioDirection,
             int videoQuality, int videoDirection) {
         mAudioQuality = audioQuality;
@@ -107,6 +117,7 @@
         mVideoDirection = videoDirection;
     }
 
+    /** @hide */
     public ImsStreamMediaProfile(int rttMode) {
         mRttMode = rttMode;
     }
@@ -178,4 +189,23 @@
         mRttMode = rttMode;
     }
 
+    public int getAudioQuality() {
+        return mAudioQuality;
+    }
+
+    public int getAudioDirection() {
+        return mAudioDirection;
+    }
+
+    public int getVideoQuality() {
+        return mVideoQuality;
+    }
+
+    public int getVideoDirection() {
+        return mVideoDirection;
+    }
+
+    public int getRttMode() {
+        return mRttMode;
+    }
 }
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
rename to telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
index 6b4479f..0552780 100644
--- a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
+++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
@@ -15,6 +15,6 @@
  */
 
 
-package com.android.ims;
+package android.telephony.ims;
 
 parcelable ImsSuppServiceNotification;
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.java b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
similarity index 76%
rename from telephony/java/com/android/ims/ImsSuppServiceNotification.java
rename to telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
index faf7499..efaade8 100644
--- a/telephony/java/com/android/ims/ImsSuppServiceNotification.java
+++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 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.
@@ -11,12 +11,13 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
 
-package com.android.ims;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,27 +29,42 @@
  *
  * @hide
  */
-public class ImsSuppServiceNotification implements Parcelable {
+@SystemApi
+public final class ImsSuppServiceNotification implements Parcelable {
     private static final String TAG = "ImsSuppServiceNotification";
 
     /** Type of notification: 0 = MO; 1 = MT */
-    public int notificationType;
+    public final int notificationType;
     /** TS 27.007 7.17 "code1" or "code2" */
-    public int code;
+    public final int code;
     /** TS 27.007 7.17 "index" - Not used currently*/
-    public int index;
+    public final int index;
     /** TS 27.007 7.17 "type" (MT only) - Not used currently */
-    public int type;
+    public final int type;
     /** TS 27.007 7.17 "number" (MT only) */
-    public String number;
+    public final String number;
     /** List of forwarded numbers, if any */
-    public String[] history;
+    public final String[] history;
 
-    public ImsSuppServiceNotification() {
+
+    public ImsSuppServiceNotification(int notificationType, int code, int index, int type,
+            String number, String[] history) {
+        this.notificationType = notificationType;
+        this.code = code;
+        this.index = index;
+        this.type = type;
+        this.number = number;
+        this.history = history;
     }
 
+    /** @hide */
     public ImsSuppServiceNotification(Parcel in) {
-        readFromParcel(in);
+        notificationType = in.readInt();
+        code = in.readInt();
+        index = in.readInt();
+        type = in.readInt();
+        number = in.readString();
+        history = in.createStringArray();
     }
 
     @Override
@@ -77,15 +93,6 @@
         out.writeStringArray(history);
     }
 
-    private void readFromParcel(Parcel in) {
-        notificationType = in.readInt();
-        code = in.readInt();
-        index = in.readInt();
-        type = in.readInt();
-        number = in.readString();
-        history = in.createStringArray();
-    }
-
     public static final Creator<ImsSuppServiceNotification> CREATOR =
             new Creator<ImsSuppServiceNotification>() {
         @Override
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..d50a0f7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of the IMS UT listener interface, which implements stubs.
+ * Override these methods to implement functionality.
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtListener {
+    private IImsUtListener mServiceInterface;
+    private static final String LOG_TAG = "ImsUtListener";
+
+    public void onUtConfigurationUpdated(int id) {
+        try {
+            mServiceInterface.utConfigurationUpdated(null, id);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdated: remote exception");
+        }
+    }
+
+    public void onUtConfigurationUpdateFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationUpdateFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdateFailed: remote exception");
+        }
+    }
+
+    public void onUtConfigurationQueried(int id, Bundle ssInfo) {
+        try {
+            mServiceInterface.utConfigurationQueried(null, id, ssInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationQueryFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationQueryFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueryFailed: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallBarringQueried(int id, ImsSsInfo[] cbInfo) {
+        try {
+            mServiceInterface.utConfigurationCallBarringQueried(null, id, cbInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallBarringQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallForwardQueried(int id, ImsCallForwardInfo[] cfInfo) {
+        try {
+            mServiceInterface.utConfigurationCallForwardQueried(null, id, cfInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallForwardQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallWaitingQueried(int id, ImsSsInfo[] cwInfo) {
+        try {
+            mServiceInterface.utConfigurationCallWaitingQueried(null, id, cwInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallWaitingQueried: remote exception");
+        }
+    }
+
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {
+        try {
+            mServiceInterface.onSupplementaryServiceIndication(ssData);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onSupplementaryServiceIndication: remote exception");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public ImsUtListener(IImsUtListener serviceInterface) {
+        mServiceInterface = serviceInterface;
+    }
+}
diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
similarity index 97%
rename from telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
rename to telephony/java/android/telephony/ims/ImsVideoCallProvider.java
index 432dc39..b4f60b9 100644
--- a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
+++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,8 +14,9 @@
  * limitations under the License
  */
 
-package com.android.ims.internal;
+package android.telephony.ims;
 
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -26,11 +27,14 @@
 import android.telecom.VideoProfile.CameraCapabilities;
 import android.view.Surface;
 
+import com.android.ims.internal.IImsVideoCallCallback;
+import com.android.ims.internal.IImsVideoCallProvider;
 import com.android.internal.os.SomeArgs;
 
 /**
  * @hide
  */
+@SystemApi
 public abstract class ImsVideoCallProvider {
     private static final int MSG_SET_CALLBACK = 1;
     private static final int MSG_SET_CAMERA = 2;
@@ -173,6 +177,7 @@
 
     /**
      * Returns binder object which can be used across IPC methods.
+     * @hide
      */
     public final IImsVideoCallProvider getInterface() {
         return mBinder;
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index 2fb6744..f25b4b1 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsConferenceState;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ImsSuppServiceNotification;
 
 /**
  * A listener type for receiving notification on IMS call session events.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
index fd2eb24..c755703 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
 /**
  * See ImsFeature#CapabilityCallback for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
similarity index 91%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 3d424a3..4433c1c 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -15,9 +15,9 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IImsConfigCallback;
 
 import com.android.ims.ImsConfigListener;
 
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
index 52efd23..2b3f1ca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
@@ -15,7 +15,7 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
 /**
  * Provides callback interface for ImsConfig when a value has changed.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
similarity index 66%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index e226ada..b9a6b3c 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
 import android.os.Message;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.feature.CapabilityChangeRequest;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.CapabilityChangeRequest;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
@@ -30,14 +30,15 @@
 import com.android.ims.internal.IImsUt;
 
 /**
- * See SmsImplBase for more information.
+ * See MmTelFeature for more information.
  * {@hide}
  */
 interface IImsMmTelFeature {
     void setListener(IImsMmTelListener l);
     int getFeatureState();
     ImsCallProfile createCallProfile(int callSessionType, int callType);
-    IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener);
+    IImsCallSession createCallSession(in ImsCallProfile profile);
+    int shouldProcessCall(in String[] uris);
     IImsUt getUtInterface();
     IImsEcbm getEcbmInterface();
     void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage);
@@ -49,4 +50,12 @@
             IImsCapabilityCallback c);
     oneway void queryCapabilityConfiguration(int capability, int radioTech,
             IImsCapabilityCallback c);
+    // SMS APIs
+    void setSmsListener(IImsSmsListener l);
+    oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
+            in byte[] pdu);
+    oneway void acknowledgeSms(int token, int messageRef, int result);
+    oneway void acknowledgeSmsReport(int token, int messageRef, int result);
+    String getSmsFormat();
+    oneway void onSmsReady();
 }
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
similarity index 86%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
index 43f5098..904e7ca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
+
+import android.os.Bundle;
 
 import com.android.ims.internal.IImsCallSession;
 
@@ -23,6 +25,6 @@
  * {@hide}
  */
 oneway interface IImsMmTelListener {
-    void onIncomingCall(IImsCallSession c);
+    void onIncomingCall(IImsCallSession c, in Bundle extras);
     void onVoiceMessageCountUpdate(int count);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
index f6005b6..691cfba 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
 /**
  * See RcsFeature for more information.
diff --git a/telephony/java/com/android/ims/internal/IImsRegistration.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
similarity index 90%
rename from telephony/java/com/android/ims/internal/IImsRegistration.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
index 6de264e..4ae0a75 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistration.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
@@ -15,9 +15,9 @@
  */
 
 
-package com.android.ims.internal;
+package android.telephony.ims.aidl;
 
-import com.android.ims.internal.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 
 /**
  * See ImsRegistration for more information.
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
similarity index 87%
rename from telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 5f21167..4f37caa 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -15,12 +15,12 @@
  */
 
 
-package com.android.ims.internal;
+package android.telephony.ims.aidl;
 
 import android.net.Uri;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
 
 /**
  * See ImsRegistrationImplBase.Callback for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
similarity index 77%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index 82a8525..86f8606 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsRegistration;
 
 /**
  * See ImsService and MmTelFeature for more information.
@@ -41,4 +41,6 @@
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
     IImsConfig getConfig(int slotId);
     IImsRegistration getRegistration(int slotId);
+    oneway void enableIms(int slotId);
+    oneway void disableIms(int slotId);
 }
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
similarity index 87%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
index 01cca2db..54f6120 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
 
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 /**
  * See ImsService#Listener for more information.
diff --git a/telephony/java/com/android/ims/internal/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
similarity index 92%
rename from telephony/java/com/android/ims/internal/IImsSmsListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
index 5a4b7e4..606df15 100644
--- a/telephony/java/com/android/ims/internal/IImsSmsListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.ims.internal;
+package android.telephony.ims.aidl;
 
 /**
  * See SmsImplBase for more information.
  * {@hide}
  */
-interface IImsSmsListener {
+oneway interface IImsSmsListener {
     void onSendSmsResult(int token, int messageRef, int status, int reason);
     void onSmsStatusReportReceived(int token, int messageRef, in String format,
             in byte[] pdu);
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..cf1efb3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,237 @@
+/*
+ * 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.telephony.ims.compat;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.telephony.ims.compat.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ *     android:permission="android.permission.BIND_IMS_SERVICE" >
+ *     <!-- Apps must declare which features they support as metadata. The different categories are
+ *     defined below. In this example, the RCS_FEATURE feature is supported. -->
+ *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     <intent-filter>
+ *         <action android:name="android.telephony.ims.compat.ImsService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ *    "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ *   available to place emergency calls at all times. This MUST be implemented by the default
+ *   ImsService provided in the device overlay.
+ *   @hide
+ */
+public class ImsService extends Service {
+
+    private static final String LOG_TAG = "ImsService(Compat)";
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService";
+
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+    /**
+     * @hide
+     */
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+        @Override
+        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+                IImsFeatureStatusCallback c) {
+            return createEmergencyMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createRcsFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            ImsService.this.removeImsFeature(slotId, featureType, c);
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService(Compat) Bound.");
+            return mImsServiceController;
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public SparseArray<ImsFeature> getFeatures(int slotId) {
+        return mFeaturesBySlot.get(slotId);
+    }
+
+    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        RcsFeature f = onCreateRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.RCS, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        f.setContext(this);
+        f.setSlotId(slotId);
+        f.addImsFeatureStatusCallback(c);
+        addImsFeature(slotId, featureType, f);
+        // TODO: Remove once new onFeatureReady AIDL is merged in.
+        f.onFeatureReady();
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.removeImsFeatureStatusCallback(c);
+            f.onFeatureRemoved();
+            features.remove(featureType);
+        }
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality. Must be able to handle emergency calls at any time as well.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of RcsFeature that will be used by the system for RCS.
+     * @hide
+     */
+    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
+        return null;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..0a12cae
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,202 @@
+/*
+ * 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.telephony.ims.compat.feature;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+    private static final String LOG_TAG = "ImsFeature";
+
+    /**
+     * Action to broadcast when ImsService is up.
+     * Internal use only.
+     * Only defined here separately compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_UP =
+            "com.android.ims.IMS_SERVICE_UP";
+
+    /**
+     * Action to broadcast when ImsService is down.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_DOWN =
+            "com.android.ims.IMS_SERVICE_DOWN";
+
+    /**
+     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+     * A long value; the phone ID corresponding to the IMS service coming up or down.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+    // Invalid feature value
+    public static final int INVALID = -1;
+    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+    // defined values in ImsServiceClass for compatibility purposes.
+    public static final int EMERGENCY_MMTEL = 0;
+    public static final int MMTEL = 1;
+    public static final int RCS = 2;
+    // Total number of features defined
+    public static final int MAX = 3;
+
+    // Integer values defining the state of the ImsFeature at any time.
+    @IntDef(flag = true,
+            value = {
+                    STATE_NOT_AVAILABLE,
+                    STATE_INITIALIZING,
+                    STATE_READY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsState {}
+    public static final int STATE_NOT_AVAILABLE = 0;
+    public static final int STATE_INITIALIZING = 1;
+    public static final int STATE_READY = 2;
+
+    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    protected Context mContext;
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public void setSlotId(int slotId) {
+        mSlotId = slotId;
+    }
+
+    public int getFeatureState() {
+        return mState;
+    }
+
+    protected final void setFeatureState(@ImsState int state) {
+        if (mState != state) {
+            mState = state;
+            notifyFeatureState(state);
+        }
+    }
+
+    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        try {
+            // If we have just connected, send queued status.
+            c.notifyImsFeatureStatus(mState);
+            // Add the callback if the callback completes successfully without a RemoteException.
+            synchronized (mStatusCallbacks) {
+                mStatusCallbacks.add(c);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+        }
+    }
+
+    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.remove(c);
+        }
+    }
+
+    /**
+     * Internal method called by ImsFeature when setFeatureState has changed.
+     * @param state
+     */
+    private void notifyFeatureState(@ImsState int state) {
+        synchronized (mStatusCallbacks) {
+            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+                 iter.hasNext(); ) {
+                IImsFeatureStatusCallback callback = iter.next();
+                try {
+                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+                    callback.notifyImsFeatureStatus(state);
+                } catch (RemoteException e) {
+                    // remove if the callback is no longer alive.
+                    iter.remove();
+                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+                }
+            }
+        }
+        sendImsServiceIntent(state);
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent(@ImsState int state) {
+        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            return;
+        }
+        Intent intent;
+        switch (state) {
+            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Called when the feature is ready to use.
+     */
+    public abstract void onFeatureReady();
+
+    /**
+     * Called when the feature is being removed and must be cleaned up.
+     */
+    public abstract void onFeatureRemoved();
+
+    /**
+     * @return Binder instance
+     */
+    public abstract IInterface getBinder();
+}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
similarity index 77%
rename from telephony/java/android/telephony/ims/feature/MMTelFeature.java
rename to telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
index 93c316f..d3d17f4 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,14 +14,13 @@
  * limitations under the License
  */
 
-package android.telephony.ims.feature;
+package android.telephony.ims.compat.feature;
 
 import android.app.PendingIntent;
 import android.os.Message;
 import android.os.RemoteException;
-import android.telephony.ims.internal.stub.SmsImplBase;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsConfig;
@@ -29,9 +28,12 @@
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsSmsListener;
 import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
 
 /**
  * Base implementation for MMTel.
@@ -110,10 +112,10 @@
         }
 
         @Override
-        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
+        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+                throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.createCallSession(sessionId, profile, listener);
+                return MMTelFeature.this.createCallSession(sessionId, profile, null);
             }
         }
 
@@ -128,7 +130,8 @@
         @Override
         public IImsUt getUtInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getUtInterface();
+                ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+                return implBase != null ? implBase.getInterface() : null;
             }
         }
 
@@ -156,7 +159,8 @@
         @Override
         public IImsEcbm getEcbmInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getEcbmInterface();
+                ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+                return implBase != null ? implBase.getImsEcbm() : null;
             }
         }
 
@@ -170,50 +174,8 @@
         @Override
         public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getMultiEndpointInterface();
-            }
-        }
-
-        @Override
-        public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.setSmsListener(l);
-            }
-        }
-
-        @Override
-        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
-                byte[] pdu) {
-            synchronized (mLock) {
-                MMTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
-            }
-        }
-
-        @Override
-        public void acknowledgeSms(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSms(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public void acknowledgeSmsReport(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public String getSmsFormat() {
-            synchronized (mLock) {
-                return MMTelFeature.this.getSmsFormat();
-            }
-        }
-
-        @Override
-        public void onSmsReady() {
-            synchronized (mLock) {
-                MMTelFeature.this.onSmsReady();
+                ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+                return implBase != null ? implBase.getIImsMultiEndpoint() : null;
             }
         }
     };
@@ -326,7 +288,6 @@
      *
      * @param sessionId a session id which is obtained from {@link #startSession}
      * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
      */
     public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
             IImsCallSessionListener listener) {
@@ -346,7 +307,7 @@
     /**
      * @return The Ut interface for the supplementary service configuration.
      */
-    public IImsUt getUtInterface() {
+    public ImsUtImplBase getUtInterface() {
         return null;
     }
 
@@ -372,7 +333,7 @@
     /**
      * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
      */
-    public IImsEcbm getEcbmInterface() {
+    public ImsEcbmImplBase getEcbmInterface() {
         return null;
     }
 
@@ -387,47 +348,10 @@
     /**
      * @return MultiEndpoint interface for DEP notifications
      */
-    public IImsMultiEndpoint getMultiEndpointInterface() {
+    public ImsMultiEndpointImplBase getMultiEndpointInterface() {
         return null;
     }
 
-    private void setSmsListener(IImsSmsListener listener) {
-        getSmsImplementation().registerSmsListener(listener);
-    }
-
-    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
-            byte[] pdu) {
-        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
-    }
-
-    private void acknowledgeSms(int token, int messageRef,
-            @SmsImplBase.DeliverStatusResult int result) {
-        getSmsImplementation().acknowledgeSms(token, messageRef, result);
-    }
-
-    private void acknowledgeSmsReport(int token, int messageRef,
-            @SmsImplBase.StatusReportResult int result) {
-        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
-    }
-
-    private void onSmsReady() {
-        getSmsImplementation().onReady();
-    }
-
-    /**
-     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
-     * non-functional implementation is returned.
-     *
-     * @return an instance of {@link SmsImplBase} which should be implemented by the IMS Provider.
-     */
-    protected SmsImplBase getSmsImplementation() {
-        return new SmsImplBase();
-    }
-
-    public String getSmsFormat() {
-        return getSmsImplementation().getSmsFormat();
-    }
-
     @Override
     public void onFeatureReady() {
 
diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
similarity index 76%
rename from telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
rename to telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
index 8d1bd9d..228b330 100644
--- a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,9 +14,10 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.compat.feature;
 
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
+
+import com.android.ims.internal.IImsRcsFeature;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -36,9 +37,8 @@
     }
 
     @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Do nothing for base implementation.
+    public void onFeatureReady() {
+
     }
 
     @Override
@@ -46,12 +46,6 @@
 
     }
 
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-
-    }
-
     @Override
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..00cb1e2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,585 @@
+/*
+ * 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.telephony.ims.compat.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+import android.telephony.ims.ImsCallSession;
+
+/**
+ * Compat implementation of ImsCallSessionImplBase for older implementations.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+    @Override
+    // convert to old implementation of listener
+    public final void setListener(IImsCallSessionListener listener)
+            throws RemoteException {
+        setListener(new ImsCallSessionListenerConverter(listener));
+    }
+
+    /**
+     * Sets the listener to listen to the session events. An {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(com.android.ims.internal.IImsCallSessionListener listener) {
+
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+     */
+    @Override
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    @Override
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    @Override
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    @Override
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state.
+     */
+    @Override
+    public int getState() {
+        return -1;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    @Override
+    public boolean isInCall() {
+        return false;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call should be muted, false otherwise.
+     */
+    @Override
+    public void setMute(boolean muted) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void start(String callee, ImsCallProfile profile) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified participants and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void startConference(String[] participants, ImsCallProfile profile) {
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see {@link ImsCallSession.Listener#callSessionStarted}
+     */
+    @Override
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void reject(int reason) {
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionTerminated}
+     */
+    @Override
+    public void terminate(int reason) {
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+     * called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see {@link ImsCallSession.Listener#callSessionHeld},
+     * {@link ImsCallSession.Listener#callSessionHoldFailed}
+     */
+    @Override
+    public void hold(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link ImsCallSession.Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+     * @see {@link ImsCallSession.Listener#callSessionResumed},
+     * {@link ImsCallSession.Listener#callSessionResumeFailed}
+     */
+    @Override
+    public void resume(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Merges the active and held call. When the merge starts,
+     * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+     * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+     * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+     * fails.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+     * {@link ImsCallSession.Listener#callSessionMergeComplete},
+     *      {@link ImsCallSession.Listener#callSessionMergeFailed}
+     */
+    @Override
+    public void merge() {
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see {@link ImsCallSession.Listener#callSessionUpdated},
+     * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+     */
+    @Override
+    public void update(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants participant list to be invited to the conference call after extending the
+     * call
+     * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+     * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+     */
+    @Override
+    public void extendToConference(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants participant list to be invited to the conference call
+     * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+     */
+    @Override
+    public void inviteParticipants(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+     */
+    @Override
+    public void removeParticipants(String[] participants) {
+    }
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void sendDtmf(char c, Message result) {
+    }
+
+    /**
+     * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void startDtmf(char c) {
+    }
+
+    /**
+     * Stop a DTMF code.
+     */
+    @Override
+    public void stopDtmf() {
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    @Override
+    public void sendUssd(String ussdMessage) {
+    }
+
+    @Override
+    public IImsVideoCallProvider getVideoCallProvider() {
+        return null;
+    }
+
+    /**
+     * Determines if the current session is multiparty.
+     * @return {@code True} if the session is multiparty.
+     */
+    @Override
+    public boolean isMultiparty() {
+        return false;
+    }
+
+    /**
+     * Device issues RTT modify request
+     * @param toProfile The profile with requested changes made
+     */
+    @Override
+    public void sendRttModifyRequest(ImsCallProfile toProfile) {
+    }
+
+    /**
+     * Device responds to Remote RTT modify request
+     * @param status true if the the request was accepted or false of the request is defined.
+     */
+    @Override
+    public void sendRttModifyResponse(boolean status) {
+    }
+
+    /**
+     * Device sends RTT message
+     * @param rttMessage RTT message to be sent
+     */
+    @Override
+    public void sendRttMessage(String rttMessage) {
+    }
+
+    /**
+     * There are two different ImsCallSessionListeners that need to reconciled here, we need to
+     * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the
+     * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling
+     * back to the framework.
+     */
+    private class ImsCallSessionListenerConverter
+            extends com.android.ims.internal.IImsCallSessionListener.Stub {
+
+        private final IImsCallSessionListener mNewListener;
+
+        public ImsCallSessionListenerConverter(IImsCallSessionListener listener) {
+            mNewListener = listener;
+        }
+
+        @Override
+        public void callSessionProgressing(IImsCallSession i,
+                ImsStreamMediaProfile imsStreamMediaProfile) throws RemoteException {
+            mNewListener.callSessionProgressing(imsStreamMediaProfile);
+        }
+
+        @Override
+        public void callSessionStarted(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionInitiated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionStartFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionInitiatedFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionTerminated(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionTerminated(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHeld(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHeld(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionHoldFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionHoldFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHoldReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHoldReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumed(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumed(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionResumeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionResumeReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumeReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionMergeStarted(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionMergeStarted(newSession, profile);
+        }
+
+        @Override
+        public void callSessionMergeComplete(IImsCallSession iImsCallSession)
+                throws RemoteException {
+            mNewListener.callSessionMergeComplete(iImsCallSession);
+        }
+
+        @Override
+        public void callSessionMergeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionMergeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdated(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionUpdateFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionUpdateFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdateReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdateReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionConferenceExtended(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionConferenceExtendFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession i,
+                IImsCallSession newSession, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionConferenceExtendReceived(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceStateUpdated(IImsCallSession i,
+                ImsConferenceState imsConferenceState) throws RemoteException {
+            mNewListener.callSessionConferenceStateUpdated(imsConferenceState);
+        }
+
+        @Override
+        public void callSessionUssdMessageReceived(IImsCallSession i, int mode, String message)
+                throws RemoteException {
+            mNewListener.callSessionUssdMessageReceived(mode, message);
+        }
+
+        @Override
+        public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+                int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+                throws RemoteException {
+            mNewListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+        }
+
+        @Override
+        public void callSessionTtyModeReceived(IImsCallSession iImsCallSession, int mode)
+                throws RemoteException {
+            mNewListener.callSessionTtyModeReceived(mode);
+        }
+
+        @Override
+        public void callSessionMultipartyStateChanged(IImsCallSession i, boolean isMultiparty)
+                throws RemoteException {
+            mNewListener.callSessionMultipartyStateChanged(isMultiparty);
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(IImsCallSession i,
+                ImsSuppServiceNotification imsSuppServiceNotification) throws RemoteException {
+            mNewListener.callSessionSuppServiceReceived(imsSuppServiceNotification);
+        }
+
+        @Override
+        public void callSessionRttModifyRequestReceived(IImsCallSession i,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionRttModifyRequestReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+            mNewListener.callSessionRttModifyResponseReceived(status);
+        }
+
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+            mNewListener.callSessionRttMessageReceived(rttMessage);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..2c325ba8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.stub;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+
+/**
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+    static final private String TAG = "ImsConfigImplBase";
+
+    ImsConfigStub mImsConfigStub;
+
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this, context);
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in Integer format.
+     */
+    public int getProvisionedValue(int item) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     */
+    public String getProvisionedStringValue(int item) throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedValue(int item, int value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived.  Synchronous blocking call.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedStringValue(int item, String value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value of the specified IMS feature item for specified network type.
+     * This operation gets the feature config value from the master storage (i.e. final
+     * value). Asynchronous non-blocking call.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param listener feature value returned asynchronously through listener.
+     */
+    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item for specified network type.
+     * This operation stores the user setting in setting db from which master db
+     * is derived.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Gets the value for IMS VoLTE provisioned.
+     * This should be the same as the operator provisioned value if applies.
+     */
+    public boolean getVolteProvisioned() throws RemoteException {
+        return false;
+    }
+
+    /**
+     * Gets the value for IMS feature item video quality.
+     *
+     * @param listener Video quality value returned asynchronously through listener.
+     */
+    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item video quality.
+     *
+     * @param quality, defines the value of video quality.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+    }
+
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+     * in order to get/set configuration parameters.
+     *
+     * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+     * with actual implementations from vendors. This class caches provisioned values from
+     * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+     * it first checks cache layer. If missed, it will call the vendor implementation of
+     * ImsConfigImplBase API.
+     * and cache the return value if the set succeeds.
+     *
+     * Provides APIs to get/set the IMS service feature/capability/parameters.
+     * The config items include:
+     * 1) Items provisioned by the operator.
+     * 2) Items configured by user. Mainly service feature class.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    static public class ImsConfigStub extends IImsConfig.Stub {
+        Context mContext;
+        WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+        private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+        private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+        @VisibleForTesting
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+            mContext = context;
+            mImsConfigImplBaseWeakReference =
+                    new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in Integer format.
+         */
+        @Override
+        public synchronized int getProvisionedValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedIntValue.get(item);
+            } else {
+                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in String format.
+         */
+        @Override
+        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedStringValue.get(item);
+            } else {
+                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                if (retVal != null) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the master value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in Integer format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+            mProvisionedIntValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            } else {
+                Log.d(TAG, "Set provision value of " + item +
+                        " to " + value + " failed with error code " + retVal);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the master value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in String format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedStringValue(int item, String value)
+                throws RemoteException {
+            mProvisionedStringValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+         */
+        @Override
+        public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().getFeatureValue(feature, network, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+         */
+        @Override
+        public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+         */
+        @Override
+        public boolean getVolteProvisioned() throws RemoteException {
+            return getImsConfigImpl().getVolteProvisioned();
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+         */
+        @Override
+        public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+            getImsConfigImpl().getVideoQuality(listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+         */
+        @Override
+        public void setVideoQuality(int quality, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setVideoQuality(quality, listener);
+        }
+
+        private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+            ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+            if (ref == null) {
+                throw new RemoteException("Fail to get ImsConfigImpl");
+            } else {
+                return ref;
+            }
+        }
+
+        private void sendImsConfigChangedIntent(int item, int value) {
+            sendImsConfigChangedIntent(item, Integer.toString(value));
+        }
+
+        private void sendImsConfigChangedIntent(int item, String value) {
+            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+            if (mContext != null) {
+                mContext.sendBroadcast(configChangedIntent);
+            }
+        }
+
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+            mProvisionedIntValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+
+        protected synchronized void updateCachedValue(
+                int item, String value, boolean notifyChange) {
+            mProvisionedStringValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
similarity index 90%
rename from telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
rename to telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
index daa74c8..b2aa080 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,15 +14,15 @@
  * limitations under the License
  */
 
-package android.telephony.ims.stub;
+package android.telephony.ims.compat.stub;
 
 import android.os.Bundle;
 import android.os.RemoteException;
 
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
 
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
similarity index 92%
rename from telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
rename to telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
index f4ec0eb..e789bd5 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
@@ -14,6 +14,6 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
 
 parcelable CapabilityChangeRequest;
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
similarity index 79%
rename from telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
rename to telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 5dbf077..7c793a5 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,8 +14,9 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -30,17 +31,32 @@
  * the request.
  * {@hide}
  */
-public class CapabilityChangeRequest implements Parcelable {
+@SystemApi
+public final class CapabilityChangeRequest implements Parcelable {
 
+    /**
+     * Contains a feature capability, defined as
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
+     * along with an associated technology, defined as
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     */
     public static class CapabilityPair {
         private final int mCapability;
         private final int radioTech;
 
-        public CapabilityPair(int capability, int radioTech) {
+        public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
             this.mCapability = capability;
             this.radioTech = radioTech;
         }
 
+        /**
+         * @hide
+         */
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
@@ -52,6 +68,9 @@
             return getRadioTech() == that.getRadioTech();
         }
 
+        /**
+         * @hide
+         */
         @Override
         public int hashCode() {
             int result = getCapability();
@@ -59,10 +78,22 @@
             return result;
         }
 
+        /**
+         * @return The stored capability, defined as
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+         */
         public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
             return mCapability;
         }
 
+        /**
+         * @return the stored radio technology, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         */
         public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
             return radioTech;
         }
@@ -73,6 +104,7 @@
     // Pair contains <radio tech, mCapability>
     private final Set<CapabilityPair> mCapabilitiesToDisable;
 
+    /** @hide */
     public CapabilityChangeRequest() {
         mCapabilitiesToEnable = new ArraySet<>();
         mCapabilitiesToDisable = new ArraySet<>();
@@ -130,6 +162,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     protected CapabilityChangeRequest(Parcel in) {
         int enableSize = in.readInt();
         mCapabilitiesToEnable = new ArraySet<>(enableSize);
@@ -177,17 +212,24 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof CapabilityChangeRequest)) return false;
 
-        CapabilityChangeRequest that = (CapabilityChangeRequest) o;
+        CapabilityChangeRequest
+                that = (CapabilityChangeRequest) o;
 
         if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
         return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int hashCode() {
         int result = mCapabilitiesToEnable.hashCode();
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index d47cea30..bfdd453 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -17,28 +17,35 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.IInterface;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 /**
- * Base class for all IMS features that are supported by the framework.
+ * Base class for all IMS features that are supported by the framework. Use a concrete subclass
+ * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
+ *
  * @hide
  */
+@SystemApi
 public abstract class ImsFeature {
 
     private static final String LOG_TAG = "ImsFeature";
@@ -46,7 +53,8 @@
     /**
      * Action to broadcast when ImsService is up.
      * Internal use only.
-     * Only defined here separately compatibility purposes with the old ImsService.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_UP =
@@ -56,6 +64,7 @@
      * Action to broadcast when ImsService is down.
      * Internal use only.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_DOWN =
@@ -65,67 +74,329 @@
      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
      * A long value; the phone ID corresponding to the IMS service coming up or down.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String EXTRA_PHONE_ID = "android:phone_id";
 
-    // Invalid feature value
-    public static final int INVALID = -1;
+    /**
+     * Invalid feature value\
+     * @hide
+     */
+    public static final int FEATURE_INVALID = -1;
     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
     // defined values in ImsServiceClass for compatibility purposes.
-    public static final int EMERGENCY_MMTEL = 0;
-    public static final int MMTEL = 1;
-    public static final int RCS = 2;
-    // Total number of features defined
-    public static final int MAX = 3;
+    /**
+     * This feature supports emergency calling over MMTEL.
+     */
+    public static final int FEATURE_EMERGENCY_MMTEL = 0;
+    /**
+     * This feature supports the MMTEL feature.
+     */
+    public static final int FEATURE_MMTEL = 1;
+    /**
+     * This feature supports the RCS feature.
+     */
+    public static final int FEATURE_RCS = 2;
+    /**
+     * Total number of features defined
+     * @hide
+     */
+    public static final int FEATURE_MAX = 3;
 
-    // Integer values defining the state of the ImsFeature at any time.
+    /**
+     * Integer values defining IMS features that are supported in ImsFeature.
+     * @hide
+     */
     @IntDef(flag = true,
             value = {
-                    STATE_NOT_AVAILABLE,
+                    FEATURE_EMERGENCY_MMTEL,
+                    FEATURE_MMTEL,
+                    FEATURE_RCS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureType {}
+
+    /**
+     * Integer values defining the state of the ImsFeature at any time.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    STATE_UNAVAILABLE,
                     STATE_INITIALIZING,
                     STATE_READY,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ImsState {}
-    public static final int STATE_NOT_AVAILABLE = 0;
+
+    /**
+     * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+     */
+    public static final int STATE_UNAVAILABLE = 0;
+    /**
+     * This {@link ImsFeature} state is initializing and should not be communicated with.
+     */
     public static final int STATE_INITIALIZING = 1;
+    /**
+     * This {@link ImsFeature} is ready for communication.
+     */
     public static final int STATE_READY = 2;
 
+    /**
+     * Integer values defining the result codes that should be returned from
+     * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    CAPABILITY_ERROR_GENERIC,
+                    CAPABILITY_SUCCESS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCapabilityError {}
+
+    /**
+     * The capability was unable to be changed.
+     */
+    public static final int CAPABILITY_ERROR_GENERIC = -1;
+    /**
+     * The capability was able to be changed.
+     */
+    public static final int CAPABILITY_SUCCESS = 0;
+
+
+    /**
+     * The framework implements this callback in order to register for Feature Capability status
+     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+     * callbacks when the ImsService can not change the capability as requested, via
+     * {@link #onChangeCapabilityConfigurationError}.
+     *
+     * @hide
+     */
+    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+            onCapabilitiesStatusChanged(new Capabilities(config));
+        }
+
+        /**
+         * Returns the result of a query for the capability configuration of a requested capability.
+         *
+         * @param capability The capability that was requested.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param isEnabled true if the capability is enabled, false otherwise.
+         */
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                boolean isEnabled) {
+
+        }
+
+        /**
+         * Called when a change to the capability configuration has returned an error.
+         *
+         * @param capability The capability that was requested to be changed.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param reason error associated with the failure to change configuration.
+         */
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+        }
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param config The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(Capabilities config) {
+        }
+    }
+
+    /**
+     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+     * provided.
+     */
+    protected static class CapabilityCallbackProxy {
+        private final IImsCapabilityCallback mCallback;
+
+        /** @hide */
+        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+            mCallback = c;
+        }
+
+        /**
+         * This method notifies the provided framework callback that the request to change the
+         * indicated capability has failed and has not changed.
+         *
+         * @param capability The Capability that will be notified to the framework, defined as
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+         * @param radioTech The radio tech that this capability failed for, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+         * @param reason The reason this capability was unable to be changed, defined as
+         * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
+         */
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+            if (mCallback == null) {
+                return;
+            }
+            try {
+                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+            }
+        }
+    }
+
+    /**
+     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+     * @hide
+     */
+    public static class Capabilities {
+        protected int mCapabilities = 0;
+
+        public Capabilities() {
+        }
+
+        protected Capabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be added to the configuration in the form of a
+         *     bit mask.
+         */
+        public void addCapabilities(int capabilities) {
+            mCapabilities |= capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be removed to the configuration in the form of a
+         *     bit mask.
+         */
+        public void removeCapabilities(int capabilities) {
+            mCapabilities &= ~capabilities;
+        }
+
+        /**
+         * @return true if all of the capabilities specified are capable.
+         */
+        public boolean isCapable(int capabilities) {
+            return (mCapabilities & capabilities) == capabilities;
+        }
+
+        /**
+         * @return a deep copy of the Capabilites.
+         */
+        public Capabilities copy() {
+            return new Capabilities(mCapabilities);
+        }
+
+        /**
+         * @return a bitmask containing the capability flags directly.
+         */
+        public int getMask() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Capabilities)) return false;
+
+            Capabilities that = (Capabilities) o;
+
+            return mCapabilities == that.mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public String toString() {
+            return "Capabilities: " + Integer.toBinaryString(mCapabilities);
+        }
+    }
+
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    /**
+     * @hide
+     */
     protected Context mContext;
+    private final Object mLock = new Object();
+    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+            = new RemoteCallbackList<>();
+    private Capabilities mCapabilityStatus = new Capabilities();
 
-    public void setContext(Context context) {
+    /**
+     * @hide
+     */
+    public final void initialize(Context context, int slotId) {
         mContext = context;
-    }
-
-    public void setSlotId(int slotId) {
         mSlotId = slotId;
     }
 
+    /**
+     * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     * @hide
+     */
     public int getFeatureState() {
-        return mState;
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        if (mState != state) {
-            mState = state;
-            notifyFeatureState(state);
+        synchronized (mLock) {
+            return mState;
         }
     }
 
-    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
+    /**
+     * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
+     * stop communication, depending on the state sent.
+     * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     */
+    public final void setFeatureState(@ImsState int state) {
+        synchronized (mLock) {
+            if (mState != state) {
+                mState = state;
+                notifyFeatureState(state);
+            }
         }
+    }
+
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
         try {
             // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(mState);
+            c.notifyImsFeatureStatus(getFeatureState());
             // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mStatusCallbacks) {
+            synchronized (mLock) {
                 mStatusCallbacks.add(c);
             }
         } catch (RemoteException e) {
@@ -133,23 +404,24 @@
         }
     }
 
-    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
-        }
-        synchronized (mStatusCallbacks) {
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        synchronized (mLock) {
             mStatusCallbacks.remove(c);
         }
     }
 
     /**
      * Internal method called by ImsFeature when setFeatureState has changed.
-     * @param state
      */
     private void notifyFeatureState(@ImsState int state) {
-        synchronized (mStatusCallbacks) {
+        synchronized (mLock) {
             for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                 iter.hasNext(); ) {
+                    iter.hasNext(); ) {
                 IImsFeatureStatusCallback callback = iter.next();
                 try {
                     Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
@@ -168,12 +440,12 @@
      * Provide backwards compatibility using deprecated service UP/DOWN intents.
      */
     private void sendImsServiceIntent(@ImsState int state) {
-        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             return;
         }
         Intent intent;
         switch (state) {
-            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_UNAVAILABLE:
             case ImsFeature.STATE_INITIALIZING:
                 intent = new Intent(ACTION_IMS_SERVICE_DOWN);
                 break;
@@ -188,17 +460,104 @@
     }
 
     /**
-     * Called when the feature is ready to use.
+     * @hide
      */
-    public abstract void onFeatureReady();
+    public final void addCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.register(c);
+    }
 
     /**
-     * Called when the feature is being removed and must be cleaned up.
+     * @hide
+     */
+    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.unregister(c);
+    }
+
+    /**
+     * @return the cached capabilities status for this feature.
+     * @hide
+     */
+    @VisibleForTesting
+    public Capabilities queryCapabilityStatus() {
+        synchronized (mLock) {
+            return mCapabilityStatus.copy();
+        }
+    }
+
+    /**
+     * Called internally to request the change of enabled capabilities.
+     * @hide
+     */
+    @VisibleForTesting
+    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+            IImsCapabilityCallback c) {
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+        }
+        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+    }
+
+    /**
+     * Called by the ImsFeature when the capabilities status has changed.
+     *
+     * @param c A {@link Capabilities} containing the new Capabilities status.
+     *
+     * @hide
+     */
+    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+        synchronized (mLock) {
+            mCapabilityStatus = c.copy();
+        }
+        int count = mCapabilityCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < count; i++) {
+                try {
+                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+                            c.mCapabilities);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+                            "callback.");
+                }
+            }
+        } finally {
+            mCapabilityCallbacks.finishBroadcast();
+        }
+    }
+
+    /**
+     * Features should override this method to receive Capability preference change requests from
+     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+     * each failed capability.
+     *
+     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+     *     enable/disable.
+     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+     * setting a subset of these capabilities fail, using
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+     */
+    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c);
+
+    /**
+     * Called when the framework is removing this feature and it needs to be cleaned up.
      */
     public abstract void onFeatureRemoved();
 
     /**
-     * @return Binder instance
+     * Called when the feature has been initialized and communication with the framework is set up.
+     * Any attempt by this feature to access the framework before this method is called will return
+     * with an {@link IllegalStateException}.
+     * The IMS provider should use this method to trigger registration for this feature on the IMS
+     * network, if needed.
      */
-    public abstract IInterface getBinder();
+    public abstract void onFeatureReady();
+
+    /**
+     * @return Binder instance that the framework will use to communicate with this feature.
+     * @hide
+     */
+    protected abstract IInterface getBinder();
 }
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
new file mode 100644
index 0000000..09267fc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -0,0 +1,672 @@
+/*
+ * 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.telephony.ims.feature;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import android.telephony.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+import android.telephony.ims.ImsCallSession;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ * @hide
+ */
+@SystemApi
+public class MmTelFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "MmTelFeature";
+
+    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+        @Override
+        public void setListener(IImsMmTelListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setListener(l);
+            }
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.getFeatureState();
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+
+        @Override
+        public ImsCallProfile createCallProfile(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.createCallProfile(callSessionType, callType);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
+            synchronized (mLock) {
+                return createCallSessionInterface(profile);
+            }
+        }
+
+        @Override
+        public int shouldProcessCall(String[] numbers) {
+            synchronized (mLock) {
+                return MmTelFeature.this.shouldProcessCall(numbers);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getUtInterface();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getEcbmInterface();
+            }
+        }
+
+        @Override
+        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getMultiEndpointInterface();
+            }
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) {
+            MmTelFeature.this.addCapabilityCallback(c);
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            MmTelFeature.this.removeCapabilityCallback(c);
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+                IImsCapabilityCallback c) throws RemoteException {
+            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) {
+            queryCapabilityConfigurationInternal(capability, radioTech, c);
+        }
+
+        @Override
+        public void setSmsListener(IImsSmsListener l) throws RemoteException {
+            MmTelFeature.this.setSmsListener(l);
+        }
+
+        @Override
+        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+                byte[] pdu) {
+            synchronized (mLock) {
+                MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
+            }
+        }
+
+        @Override
+        public void acknowledgeSms(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSms(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public void acknowledgeSmsReport(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public String getSmsFormat() {
+            synchronized (mLock) {
+                return MmTelFeature.this.getSmsFormat();
+            }
+        }
+
+        @Override
+        public void onSmsReady() {
+            synchronized (mLock) {
+                MmTelFeature.this.onSmsReady();
+            }
+        }
+    };
+
+    /**
+     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+     * The capabilities that are used in MmTelFeature are defined as
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+     *
+     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
+     * {@link #queryCapabilityStatus()}.
+     *
+     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
+     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
+     * status can also be queried using {@link #queryCapabilityStatus()}.
+     */
+    public static class MmTelCapabilities extends Capabilities {
+
+        /**
+         * @hide
+         */
+        @VisibleForTesting
+        public MmTelCapabilities() {
+            super();
+        }
+
+        public MmTelCapabilities(Capabilities c) {
+            mCapabilities = c.mCapabilities;
+        }
+
+        public MmTelCapabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        @IntDef(flag = true,
+                value = {
+                        CAPABILITY_TYPE_VOICE,
+                        CAPABILITY_TYPE_VIDEO,
+                        CAPABILITY_TYPE_UT,
+                        CAPABILITY_TYPE_SMS
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface MmTelCapability {}
+
+        /**
+         * This MmTelFeature supports Voice calling (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+        /**
+         * This MmTelFeature supports Video (IR.94)
+         */
+        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+        /**
+         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+        /**
+         * This MmTelFeature supports SMS (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+
+        @Override
+        public final void addCapabilities(@MmTelCapability int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        @Override
+        public final void removeCapabilities(@MmTelCapability int capability) {
+            super.removeCapabilities(capability);
+        }
+
+        @Override
+        public final boolean isCapable(@MmTelCapability int capabilities) {
+            return super.isCapable(capabilities);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
+            builder.append("Voice: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VOICE));
+            builder.append(" Video: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
+            builder.append(" UT: ");
+            builder.append(isCapable(CAPABILITY_TYPE_UT));
+            builder.append(" SMS: ");
+            builder.append(isCapable(CAPABILITY_TYPE_SMS));
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Listener that the framework implements for communication from the MmTelFeature.
+     * @hide
+     */
+    public static class Listener extends IImsMmTelListener.Stub {
+
+        /**
+         * Called when the IMS provider receives an incoming call.
+         * @param c The {@link ImsCallSession} associated with the new call.
+         */
+        @Override
+        public void onIncomingCall(IImsCallSession c, Bundle extras) {
+
+        }
+
+        /**
+         * Updates the Listener when the voice message count for IMS has changed.
+         * @param count an integer representing the new message count.
+         */
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+
+        }
+    }
+
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+     * outgoing call as IMS.
+     */
+    public static final int PROCESS_CALL_IMS = 0;
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+     * not process the outgoing NON_EMERGENCY call as IMS and should instead use circuit switch.
+     */
+    public static final int PROCESS_CALL_CSFB = 1;
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+     * not process the outgoing EMERGENCY call as IMS and should instead use circuit switch.
+     */
+    public static final int PROCESS_CALL_EMERGENCY_CSFB = 2;
+
+    @IntDef(flag = true,
+            value = {
+                    PROCESS_CALL_IMS,
+                    PROCESS_CALL_CSFB,
+                    PROCESS_CALL_EMERGENCY_CSFB
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessCallResult {}
+
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    private IImsMmTelListener mListener;
+
+    /**
+     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+     *     notifies the framework.
+     */
+    private void setListener(IImsMmTelListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+        if (mListener != null) {
+            onFeatureReady();
+        }
+    }
+
+    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
+    /**
+     * The current capability status that this MmTelFeature has defined is available. This
+     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+     * available to be used.
+     *
+     * Should be a subset of the capabilities that are enabled by the framework in
+     * {@link #changeEnabledCapabilities}.
+     * @return A copy of the current MmTelFeature capability status.
+     */
+    @Override
+    public final MmTelCapabilities queryCapabilityStatus() {
+        return new MmTelCapabilities(super.queryCapabilityStatus());
+    }
+
+    /**
+     * Notify the framework that the status of the Capabilities has changed. Even though the
+     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+     * the feature being unavailable from the network.
+     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+     * the status of that capability is disabled. This can happen if the network does not currently
+     * support the capability that is enabled. A capability that is disabled by the framework (via
+     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+     */
+    public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+        super.notifyCapabilitiesStatusChanged(c);
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+     */
+    public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c.getServiceImpl(), extras);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     *
+     * @hide
+     */
+    public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c, extras);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Notify the framework of a change in the Voice Message count.
+     * @link count the new Voice Message count.
+     */
+    public final void notifyVoiceMessageCountUpdate(int count) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onVoiceMessageCountUpdate(count);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     */
+    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base implementation - Override to provide functionality
+        return false;
+    }
+
+    /**
+     * The MmTelFeature should override this method to handle the enabling/disabling of
+     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+     * could not be set to their new values,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+     * individually for each capability whose processing resulted in an error.
+     *
+     * Enabling/Disabling a capability here indicates that the capability should be registered or
+     * deregistered (depending on the capability change) and become available or unavailable to
+     * the framework.
+     */
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Base implementation, no-op
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     */
+    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+            throws RemoteException {
+        ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+        return s != null ? s.getServiceImpl() : null;
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param profile a call profile to make the call
+     */
+    public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Called by the framework to determine if the outgoing call, designated by the outgoing
+     * {@link Uri}s, should be processed as an IMS call or CSFB call.
+     * @param numbers An array of {@link String}s that will be used for placing the call. There can
+     *         be multiple {@link String}s listed in the case when we want to place an outgoing
+     *         call as a conference.
+     * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
+     *        call wil lbe placed over IMS or via CSFB.
+     */
+    public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+        return PROCESS_CALL_IMS;
+    }
+
+    /**
+     *
+     * @hide
+     */
+    protected IImsUt getUtInterface() throws RemoteException {
+        ImsUtImplBase utImpl = getUt();
+        return utImpl != null ? utImpl.getInterface() : null;
+    }
+
+    /**
+     * @hide
+     */
+    protected IImsEcbm getEcbmInterface() throws RemoteException {
+        ImsEcbmImplBase ecbmImpl = getEcbm();
+        return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+        return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+    }
+
+    /**
+     * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+     * configuration.
+     */
+    public ImsUtImplBase getUt() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
+     * calls that support it.
+     */
+    public ImsEcbmImplBase getEcbm() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+     * package processing for multi-endpoint.
+     */
+    public ImsMultiEndpointImplBase getMultiEndpoint() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MmTelFeature.
+     * @param mode An integer containing the new UI TTY Mode, can consist of
+     *         {@link TelecomManager#TTY_MODE_OFF},
+     *         {@link TelecomManager#TTY_MODE_FULL},
+     *         {@link TelecomManager#TTY_MODE_HCO},
+     *         {@link TelecomManager#TTY_MODE_VCO}
+     * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
+     */
+    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+        // Base Implementation - Should be overridden
+    }
+
+    private void setSmsListener(IImsSmsListener listener) {
+        getSmsImplementation().registerSmsListener(listener);
+    }
+
+    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
+    }
+
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result) {
+        getSmsImplementation().acknowledgeSms(token, messageRef, result);
+    }
+
+    private void acknowledgeSmsReport(int token, int messageRef,
+            @ImsSmsImplBase.StatusReportResult int result) {
+        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
+    }
+
+    private void onSmsReady() {
+        getSmsImplementation().onReady();
+    }
+
+    /**
+     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
+     * non-functional implementation is returned.
+     *
+     * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
+     * Provider.
+     */
+    public ImsSmsImplBase getSmsImplementation() {
+        return new ImsSmsImplBase();
+    }
+
+    private String getSmsFormat() {
+        return getSmsImplementation().getSmsFormat();
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureRemoved() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMmTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 40c5181..a637e16 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -16,16 +16,18 @@
 
 package android.telephony.ims.feature;
 
-import com.android.ims.internal.IImsRcsFeature;
+import android.annotation.SystemApi;
+import android.telephony.ims.aidl.IImsRcsFeature;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
  * this class and provide implementations of the RcsFeature methods that they support.
  * @hide
  */
-
+@SystemApi
 public class RcsFeature extends ImsFeature {
 
+    /**{@inheritDoc}*/
     private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
         // Empty Default Implementation.
     };
@@ -35,16 +37,30 @@
         super();
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public void onFeatureReady() {
-
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Do nothing for base implementation.
     }
 
+    /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
 
     }
 
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * @hide
+     */
     @Override
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
deleted file mode 100644
index 5d16dd5..0000000
--- a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
+++ /dev/null
@@ -1,364 +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.telephony.ims.internal;
-
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.ImsCallSession;
-
-/**
- * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListener {
-
-    private final IImsCallSessionListener mListener;
-
-    public ImsCallSessionListener(IImsCallSessionListener l) {
-        mListener = l;
-    }
-
-    /**
-     * Called when a request is sent out to initiate a new session
-     * and 1xx response is received from the network.
-     */
-    public void callSessionProgressing(ImsStreamMediaProfile profile)
-            throws RemoteException {
-        mListener.callSessionProgressing(profile);
-    }
-
-    /**
-     * Called when the session is initiated.
-     *
-     * @param profile the associated {@link ImsCallSession}.
-     */
-    public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionInitiated(profile);
-    }
-
-    /**
-     * Called when the session establishment has failed.
-     *
-     * @param reasonInfo detailed reason of the session establishment failure
-     */
-    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionInitiatedFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is terminated.
-     *
-     * @param reasonInfo detailed reason of the session termination
-     */
-    public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionTerminated(reasonInfo);
-    }
-
-    /**
-     * Called when the session is on hold.
-     */
-    public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHeld(profile);
-    }
-
-    /**
-     * Called when the session hold has failed.
-     *
-     * @param reasonInfo detailed reason of the session hold failure
-     */
-    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHoldFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session hold is received from the remote user.
-     */
-    public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHoldReceived(profile);
-    }
-
-    /**
-     * Called when the session resume is done.
-     */
-    public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumed(profile);
-    }
-
-    /**
-     * Called when the session resume has failed.
-     *
-     * @param reasonInfo detailed reason of the session resume failure
-     */
-    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionResumeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session resume is received from the remote user.
-     */
-    public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumeReceived(profile);
-    }
-
-    /**
-     * Called when the session merge has been started.  At this point, the {@code newSession}
-     * represents the session which has been initiated to the IMS conference server for the
-     * new merged conference.
-     *
-     * @param newSession the session object that is merged with an active & hold session
-     */
-    public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the session merge is successful and the merged session is active.
-     *
-     * @param newSession the new session object that is used for the conference
-     */
-    public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
-        mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
-    }
-
-    /**
-     * Called when the session merge has failed.
-     *
-     * @param reasonInfo detailed reason of the call merge failure
-     */
-    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionMergeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is updated (except for hold/unhold).
-     */
-    public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdated(profile);
-    }
-
-    /**
-     * Called when the session update has failed.
-     *
-     * @param reasonInfo detailed reason of the session update failure
-     */
-    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionUpdateFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session update is received from the remote user.
-     */
-    public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdateReceived(profile);
-    }
-
-    /**
-     * Called when the session has been extended to a conference session.
-     *
-     * @param newSession the session object that is extended to the conference
-     *      from the active session
-     */
-    public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the conference extension has failed.
-     *
-     * @param reasonInfo detailed reason of the conference extension failure
-     */
-    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionConferenceExtendFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the conference extension is received from the remote user.
-     */
-    public void callSessionConferenceExtendReceived(ImsCallSession newSession,
-            ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionConferenceExtendReceived(newSession != null
-                ? newSession.getSession() : null, profile);
-    }
-
-    /**
-     * Called when the invitation request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the invitation request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference invitation failure
-     */
-    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the removal request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionRemoveParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the removal request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference removal failure
-     */
-    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Notifies the framework of the updated Call session conference state.
-     *
-     * @param state the new {@link ImsConferenceState} associated with the conference.
-     */
-    public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
-        mListener.callSessionConferenceStateUpdated(state);
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
-            throws RemoteException {
-        mListener.callSessionUssdMessageReceived(mode, ussdMessage);
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
-     * handover from one radio technology to another.
-     *
-     * @param srcAccessTech    The source radio access technology; one of the access technology
-     *                         constants defined in {@link android.telephony.ServiceState}.  For
-     *                         example
-     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     * @param targetAccessTech The target radio access technology; one of the access technology
-     *                         constants defined in {@link android.telephony.ServiceState}.  For
-     *                         example
-     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     */
-    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
-            throws RemoteException {
-        mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
-    }
-
-    /**
-     * Called when session access technology changes.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo
-     */
-    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when session access technology change fails.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo handover failure reason
-     */
-    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when the TTY mode is changed by the remote party.
-     *
-     * @param mode one of the following: -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
-     */
-    public void callSessionTtyModeReceived(int mode) throws RemoteException {
-        mListener.callSessionTtyModeReceived(mode);
-    }
-
-    /**
-     * Called when the multiparty state is changed for this {@code ImsCallSession}.
-     *
-     * @param isMultiParty {@code true} if the session became multiparty,
-     *                     {@code false} otherwise.
-     */
-
-    public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
-        mListener.callSessionMultipartyStateChanged(isMultiParty);
-    }
-
-    /**
-     * Called when the supplementary service information is received for the current session.
-     */
-    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
-            throws RemoteException {
-        mListener.callSessionSuppServiceReceived(suppSrvNotification);
-    }
-
-    /**
-     * Received RTT modify request from the remote party.
-     *
-     * @param callProfile ImsCallProfile with updated attributes
-     */
-    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
-            throws RemoteException {
-        mListener.callSessionRttModifyRequestReceived(callProfile);
-    }
-
-    /**
-     * @param status the received response for RTT modify request.
-     */
-    public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
-        mListener.callSessionRttModifyResponseReceived(status);
-    }
-
-    /**
-     * Device received RTT message from Remote UE.
-     *
-     * @param rttMessage RTT message received
-     */
-    public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
-        mListener.callSessionRttMessageReceived(rttMessage);
-    }
-}
-
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
deleted file mode 100644
index afaf332..0000000
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ /dev/null
@@ -1,339 +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.telephony.ims.internal;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsServiceController;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.feature.RcsFeature;
-import android.telephony.ims.internal.stub.ImsConfigImplBase;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsRegistration;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
- * ImsService must register the service in their AndroidManifest to be detected by the framework.
- * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
- * permission. Then, the ImsService definition in the manifest must follow the following format:
- *
- * ...
- * <service android:name=".EgImsService"
- *     android:permission="android.permission.BIND_IMS_SERVICE" >
- *     <!-- Apps must declare which features they support as metadata. The different categories are
- *     defined below. In this example, the RCS_FEATURE feature is supported. -->
- *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
- *     <intent-filter>
- *         <action android:name="android.telephony.ims.ImsService" />
- *     </intent-filter>
- * </service>
- * ...
- *
- * The telephony framework will then bind to the ImsService you have defined in your manifest
- * if you are either:
- * 1) Defined as the default ImsService for the device in the device overlay using
- *    "config_ims_package".
- * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
- *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
- *
- * The features that are currently supported in an ImsService are:
- * - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
- *   @hide
- */
-public class ImsService extends Service {
-
-    private static final String LOG_TAG = "ImsService";
-
-    /**
-     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
-     * @hide
-     */
-    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
-
-    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
-    // slot.
-    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
-    // call ImsFeature#onFeatureRemoved.
-    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
-
-    private IImsServiceControllerListener mListener;
-
-
-    /**
-     * Listener that notifies the framework of ImsService changes.
-     */
-    public static class Listener extends IImsServiceControllerListener.Stub {
-        /**
-         * The IMS features that this ImsService supports has changed.
-         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
-         *   that this ImsService supports. This may trigger the addition/removal of feature
-         *   in this service.
-         */
-        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
-        }
-    }
-
-    /**
-     * @hide
-     */
-    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
-        @Override
-        public void setListener(IImsServiceControllerListener l) {
-            mListener = l;
-        }
-
-        @Override
-        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMmTelFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createRcsFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            ImsService.this.removeImsFeature(slotId, featureType, c);
-        }
-
-        @Override
-        public ImsFeatureConfiguration querySupportedImsFeatures() {
-            return ImsService.this.querySupportedImsFeatures();
-        }
-
-        @Override
-        public void notifyImsServiceReadyForFeatureCreation() {
-            ImsService.this.readyForFeatureCreation();
-        }
-
-        @Override
-        public void notifyImsFeatureReady(int slotId, int featureType)
-                throws RemoteException {
-            ImsService.this.notifyImsFeatureReady(slotId, featureType);
-        }
-
-        @Override
-        public IImsConfig getConfig(int slotId) throws RemoteException {
-            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
-            return c != null ? c.getBinder() : null;
-        }
-
-        @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
-            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
-            return r != null ? r.getBinder() : null;
-        }
-    };
-
-    /**
-     * @hide
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        if(SERVICE_INTERFACE.equals(intent.getAction())) {
-            Log.i(LOG_TAG, "ImsService Bound.");
-            return mImsServiceController;
-        }
-        return null;
-    }
-
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public SparseArray<ImsFeature> getFeatures(int slotId) {
-        return mFeaturesBySlot.get(slotId);
-    }
-
-    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MmTelFeature f = createMmTelFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private IImsRcsFeature createRcsFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        RcsFeature f = createRcsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private void setupFeature(ImsFeature f, int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        f.addImsFeatureStatusCallback(c);
-        f.initialize(this, slotId);
-        addImsFeature(slotId, featureType, f);
-    }
-
-    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
-        synchronized (mFeaturesBySlot) {
-            // Get SparseArray for Features, by querying slot Id
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                // Populate new SparseArray of features if it doesn't exist for this slot yet.
-                features = new SparseArray<>();
-                mFeaturesBySlot.put(slotId, features);
-            }
-            features.put(featureType, f);
-        }
-    }
-
-    private void removeImsFeature(int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
-                        + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.removeImsFeatureStatusCallback(c);
-            f.onFeatureRemoved();
-            features.remove(featureType);
-        }
-    }
-
-    private void notifyImsFeatureReady(int slotId, int featureType) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
-                        "slot " + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.onFeatureReady();
-        }
-    }
-
-    /**
-     * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
-     * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
-     * to the {@link ImsFeature.FeatureType}s configured here.
-     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
-     * defined in {@link ImsFeature.FeatureType}.
-     */
-    public ImsFeatureConfiguration querySupportedImsFeatures() {
-        // Return empty for base implementation
-        return new ImsFeatureConfiguration();
-    }
-
-    /**
-     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
-     * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
-     * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
-     */
-    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
-            throws RemoteException {
-        if (mListener == null) {
-            throw new IllegalStateException("Framework is not ready");
-        }
-        mListener.onUpdateSupportedImsFeatures(c);
-    }
-
-    /**
-     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
-     * the ImsService has registered for with the framework, either in the manifest or via
-     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
-     * feature initialization because the framework may bind to this service multiple times to
-     * query the ImsService's {@link ImsFeatureConfiguration} via
-     * {@link #querySupportedImsFeatures()}before creating features.
-     */
-    public void readyForFeatureCreation() {
-    }
-
-    /**
-     * When called, the framework is requesting that a new MmTelFeature is created for the specified
-     * slot.
-     *
-     * @param slotId The slot ID that the MMTel Feature is being created for.
-     * @return The newly created MmTelFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public MmTelFeature createMmTelFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * When called, the framework is requesting that a new RcsFeature is created for the specified
-     * slot
-     *
-     * @param slotId The slot ID that the RCS Feature is being created for.
-     * @return The newly created RcsFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public RcsFeature createRcsFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * @param slotId The slot that the IMS configuration is associated with.
-     * @return ImsConfig implementation that is associated with the specified slot.
-     */
-    public ImsConfigImplBase getConfig(int slotId) {
-        return new ImsConfigImplBase();
-    }
-
-    /**
-     * @param slotId The slot that is associated with the IMS Registration.
-     * @return the ImsRegistration implementation associated with the slot.
-     */
-    public ImsRegistrationImplBase getRegistration(int slotId) {
-        return new ImsRegistrationImplBase();
-    }
-}
diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
deleted file mode 100644
index 9f82ad2..0000000
--- a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
+++ /dev/null
@@ -1,462 +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.telephony.ims.internal.feature;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IInterface;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.util.Log;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-/**
- * Base class for all IMS features that are supported by the framework.
- *
- * @hide
- */
-public abstract class ImsFeature {
-
-    private static final String LOG_TAG = "ImsFeature";
-
-    /**
-     * Action to broadcast when ImsService is up.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_UP =
-            "com.android.ims.IMS_SERVICE_UP";
-
-    /**
-     * Action to broadcast when ImsService is down.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_DOWN =
-            "com.android.ims.IMS_SERVICE_DOWN";
-
-    /**
-     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
-     * A long value; the phone ID corresponding to the IMS service coming up or down.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PHONE_ID = "android:phone_id";
-
-    // Invalid feature value
-    public static final int FEATURE_INVALID = -1;
-    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
-    // defined values in ImsServiceClass for compatibility purposes.
-    public static final int FEATURE_EMERGENCY_MMTEL = 0;
-    public static final int FEATURE_MMTEL = 1;
-    public static final int FEATURE_RCS = 2;
-    // Total number of features defined
-    public static final int FEATURE_MAX = 3;
-
-    // Integer values defining IMS features that are supported in ImsFeature.
-    @IntDef(flag = true,
-            value = {
-                    FEATURE_EMERGENCY_MMTEL,
-                    FEATURE_MMTEL,
-                    FEATURE_RCS
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FeatureType {}
-
-    // Integer values defining the state of the ImsFeature at any time.
-    @IntDef(flag = true,
-            value = {
-                    STATE_UNAVAILABLE,
-                    STATE_INITIALIZING,
-                    STATE_READY,
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsState {}
-
-    public static final int STATE_UNAVAILABLE = 0;
-    public static final int STATE_INITIALIZING = 1;
-    public static final int STATE_READY = 2;
-
-    // Integer values defining the result codes that should be returned from
-    // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
-    @IntDef(flag = true,
-            value = {
-                    CAPABILITY_ERROR_GENERIC,
-                    CAPABILITY_SUCCESS
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsCapabilityError {}
-
-    public static final int CAPABILITY_ERROR_GENERIC = -1;
-    public static final int CAPABILITY_SUCCESS = 0;
-
-
-    /**
-     * The framework implements this callback in order to register for Feature Capability status
-     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
-     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
-     * callbacks when the ImsService can not change the capability as requested, via
-     * {@link #onChangeCapabilityConfigurationError}.
-     */
-    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
-        @Override
-        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
-            onCapabilitiesStatusChanged(new Capabilities(config));
-        }
-
-        /**
-         * Returns the result of a query for the capability configuration of a requested capability.
-         *
-         * @param capability The capability that was requested.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param isEnabled true if the capability is enabled, false otherwise.
-         */
-        @Override
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-
-        }
-
-        /**
-         * Called when a change to the capability configuration has returned an error.
-         *
-         * @param capability The capability that was requested to be changed.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param reason error associated with the failure to change configuration.
-         */
-        @Override
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                int reason) {
-        }
-
-        /**
-         * The status of the feature's capabilities has changed to either available or unavailable.
-         * If unavailable, the feature is not able to support the unavailable capability at this
-         * time.
-         *
-         * @param config The new availability of the capabilities.
-         */
-        public void onCapabilitiesStatusChanged(Capabilities config) {
-        }
-    }
-
-    /**
-     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
-     * provided.
-     */
-    protected static class CapabilityCallbackProxy {
-        private final IImsCapabilityCallback mCallback;
-
-        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
-            mCallback = c;
-        }
-
-        /**
-         * This method notifies the provided framework callback that the request to change the
-         * indicated capability has failed and has not changed.
-         *
-         * @param capability The Capability that will be notified to the framework.
-         * @param radioTech The radio tech that this capability failed for.
-         * @param reason The reason this capability was unable to be changed.
-         */
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                @ImsCapabilityError int reason) {
-            try {
-                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
-            }
-        }
-
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-            try {
-                mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
-            }
-        }
-    }
-
-    /**
-     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
-     */
-    public static class Capabilities {
-        protected int mCapabilities = 0;
-
-        public Capabilities() {
-        }
-
-        protected Capabilities(int capabilities) {
-            mCapabilities = capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be added to the configuration in the form of a
-         *     bit mask.
-         */
-        public void addCapabilities(int capabilities) {
-            mCapabilities |= capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be removed to the configuration in the form of a
-         *     bit mask.
-         */
-        public void removeCapabilities(int capabilities) {
-            mCapabilities &= ~capabilities;
-        }
-
-        /**
-         * @return true if all of the capabilities specified are capable.
-         */
-        public boolean isCapable(int capabilities) {
-            return (mCapabilities & capabilities) == capabilities;
-        }
-
-        public Capabilities copy() {
-            return new Capabilities(mCapabilities);
-        }
-
-        /**
-         * @return a bitmask containing the capability flags directly.
-         */
-        public int getMask() {
-            return mCapabilities;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof Capabilities)) return false;
-
-            Capabilities that = (Capabilities) o;
-
-            return mCapabilities == that.mCapabilities;
-        }
-
-        @Override
-        public int hashCode() {
-            return mCapabilities;
-        }
-    }
-
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
-            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_UNAVAILABLE;
-    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private Context mContext;
-    private final Object mLock = new Object();
-    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
-            = new RemoteCallbackList<>();
-    private Capabilities mCapabilityStatus = new Capabilities();
-
-    public final void initialize(Context context, int slotId) {
-        mContext = context;
-        mSlotId = slotId;
-    }
-
-    public final int getFeatureState() {
-        synchronized (mLock) {
-            return mState;
-        }
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            if (mState != state) {
-                mState = state;
-                notifyFeatureState(state);
-            }
-        }
-    }
-
-    // Not final for testing, but shouldn't be extended!
-    @VisibleForTesting
-    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        try {
-            // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(getFeatureState());
-            // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mLock) {
-                mStatusCallbacks.add(c);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-        }
-    }
-
-    @VisibleForTesting
-    // Not final for testing, but should not be extended!
-    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        synchronized (mLock) {
-            mStatusCallbacks.remove(c);
-        }
-    }
-
-    /**
-     * Internal method called by ImsFeature when setFeatureState has changed.
-     */
-    private void notifyFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                    iter.hasNext(); ) {
-                IImsFeatureStatusCallback callback = iter.next();
-                try {
-                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
-                    callback.notifyImsFeatureStatus(state);
-                } catch (RemoteException e) {
-                    // remove if the callback is no longer alive.
-                    iter.remove();
-                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-                }
-            }
-        }
-        sendImsServiceIntent(state);
-    }
-
-    /**
-     * Provide backwards compatibility using deprecated service UP/DOWN intents.
-     */
-    private void sendImsServiceIntent(@ImsState int state) {
-        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            return;
-        }
-        Intent intent;
-        switch (state) {
-            case ImsFeature.STATE_UNAVAILABLE:
-            case ImsFeature.STATE_INITIALIZING:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-                break;
-            case ImsFeature.STATE_READY:
-                intent = new Intent(ACTION_IMS_SERVICE_UP);
-                break;
-            default:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-        }
-        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
-        mContext.sendBroadcast(intent);
-    }
-
-    public final void addCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.register(c);
-    }
-
-    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.unregister(c);
-    }
-
-    /**
-     * @return the cached capabilities status for this feature.
-     */
-    @VisibleForTesting
-    public Capabilities queryCapabilityStatus() {
-        synchronized (mLock) {
-            return mCapabilityStatus.copy();
-        }
-    }
-
-    // Called internally to request the change of enabled capabilities.
-    @VisibleForTesting
-    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
-            IImsCapabilityCallback c) throws RemoteException {
-        if (request == null) {
-            throw new IllegalArgumentException(
-                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
-        }
-        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
-    }
-
-    /**
-     * Called by the ImsFeature when the capabilities status has changed.
-     *
-     * @param c A {@link Capabilities} containing the new Capabilities status.
-     */
-    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
-        synchronized (mLock) {
-            mCapabilityStatus = c.copy();
-        }
-        int count = mCapabilityCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < count; i++) {
-                try {
-                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
-                            c.mCapabilities);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
-                            "callback.");
-                }
-            }
-        } finally {
-            mCapabilityCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * Features should override this method to receive Capability preference change requests from
-     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
-     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
-     * each failed capability.
-     *
-     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
-     *     enable/disable.
-     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
-     * setting a subset of these capabilities fail, using
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
-     */
-    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c);
-
-    /**
-     * Called when the framework is removing this feature and it needs to be cleaned up.
-     */
-    public abstract void onFeatureRemoved();
-
-    /**
-     * Called when the feature has been initialized and communication with the framework is set up.
-     * Any attempt by this feature to access the framework before this method is called will return
-     * with an {@link IllegalStateException}.
-     * The IMS provider should use this method to trigger registration for this feature on the IMS
-     * network, if needed.
-     */
-    public abstract void onFeatureReady();
-
-    /**
-     * @return Binder instance that the framework will use to communicate with this feature.
-     */
-    protected abstract IInterface getBinder();
-}
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
deleted file mode 100644
index 9b576c7..0000000
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ /dev/null
@@ -1,437 +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.telephony.ims.internal.feature;
-
-import android.annotation.IntDef;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telecom.TelecomManager;
-import android.telephony.ims.internal.ImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsEcbmImplBase;
-import android.telephony.ims.stub.ImsMultiEndpointImplBase;
-import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
- *
- * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
- * service supports.
- * @hide
- */
-
-public class MmTelFeature extends ImsFeature {
-
-    private static final String LOG_TAG = "MmTelFeature";
-
-    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
-
-        @Override
-        public void setListener(IImsMmTelListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setListener(l);
-            }
-        }
-
-        @Override
-        public int getFeatureState() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getFeatureState();
-            }
-        }
-
-
-        @Override
-        public ImsCallProfile createCallProfile(int callSessionType, int callType)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.createCallProfile(callSessionType,  callType);
-            }
-        }
-
-        @Override
-        public IImsCallSession createCallSession(ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
-            synchronized (mLock) {
-                ImsCallSession s = MmTelFeature.this.createCallSession(profile,
-                        new ImsCallSessionListener(listener));
-                return s != null ? s.getSession() : null;
-            }
-        }
-
-        @Override
-        public IImsUt getUtInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getUt();
-            }
-        }
-
-        @Override
-        public IImsEcbm getEcbmInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getEcbm();
-            }
-        }
-
-        @Override
-        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
-            }
-        }
-
-        @Override
-        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getMultiEndpoint();
-            }
-        }
-
-        @Override
-        public int queryCapabilityStatus() throws RemoteException {
-            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
-        }
-
-        @Override
-        public void addCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.addCapabilityCallback(c);
-        }
-
-        @Override
-        public void removeCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.removeCapabilityCallback(c);
-        }
-
-        @Override
-        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
-                IImsCapabilityCallback c) throws RemoteException {
-            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
-        }
-
-        @Override
-        public void queryCapabilityConfiguration(int capability, int radioTech,
-                IImsCapabilityCallback c) {
-            queryCapabilityConfigurationInternal(capability, radioTech, c);
-        }
-    };
-
-    /**
-     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
-     * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
-     *
-     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
-     * {@link #queryCapabilityStatus()}.
-     *
-     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
-     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
-     * status can also be queried using {@link #queryCapabilityStatus()}.
-     */
-    public static class MmTelCapabilities extends Capabilities {
-
-        @VisibleForTesting
-        public MmTelCapabilities() {
-            super();
-        }
-
-        public MmTelCapabilities(Capabilities c) {
-            mCapabilities = c.mCapabilities;
-        }
-
-        @IntDef(flag = true,
-                value = {
-                        CAPABILITY_TYPE_VOICE,
-                        CAPABILITY_TYPE_VIDEO,
-                        CAPABILITY_TYPE_UT,
-                        CAPABILITY_TYPE_SMS
-                })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface MmTelCapability {}
-
-        /**
-         * This MmTelFeature supports Voice calling (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
-
-        /**
-         * This MmTelFeature supports Video (IR.94)
-         */
-        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
-
-        /**
-         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_UT = 1 << 2;
-
-        /**
-         * This MmTelFeature supports SMS (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_SMS = 1 << 3;
-
-        @Override
-        public final void addCapabilities(@MmTelCapability int capabilities) {
-            super.addCapabilities(capabilities);
-        }
-
-        @Override
-        public final void removeCapabilities(@MmTelCapability int capability) {
-            super.removeCapabilities(capability);
-        }
-
-        @Override
-        public final boolean isCapable(@MmTelCapability int capabilities) {
-            return super.isCapable(capabilities);
-        }
-    }
-
-    /**
-     * Listener that the framework implements for communication from the MmTelFeature.
-     */
-    public static class Listener extends IImsMmTelListener.Stub {
-
-        @Override
-        public final void onIncomingCall(IImsCallSession c) {
-            onIncomingCall(new ImsCallSession(c));
-        }
-
-        /**
-         * Updates the Listener when the voice message count for IMS has changed.
-         * @param count an integer representing the new message count.
-         */
-        @Override
-        public void onVoiceMessageCountUpdate(int count) {
-
-        }
-
-        /**
-         * Called when the IMS provider receives an incoming call.
-         * @param c The {@link ImsCallSession} associated with the new call.
-         */
-        public void onIncomingCall(ImsCallSession c) {
-        }
-    }
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
-    private IImsMmTelListener mListener;
-
-    /**
-     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
-     *     notifies the framework.
-     */
-    private void setListener(IImsMmTelListener listener) {
-        synchronized (mLock) {
-            mListener = listener;
-        }
-    }
-
-    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
-            IImsCapabilityCallback c) {
-        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
-        try {
-            if (c != null) {
-                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
-            }
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
-        }
-    }
-
-    /**
-     * The current capability status that this MmTelFeature has defined is available. This
-     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
-     * available to be used.
-     *
-     * Should be a subset of the capabilities that are enabled by the framework in
-     * {@link #changeEnabledCapabilities}.
-     * @return A copy of the current MmTelFeature capability status.
-     */
-    @Override
-    public final MmTelCapabilities queryCapabilityStatus() {
-        return new MmTelCapabilities(super.queryCapabilityStatus());
-    }
-
-    /**
-     * Notify the framework that the status of the Capabilities has changed. Even though the
-     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
-     * the feature being unavailable from the network.
-     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
-     * the status of that capability is disabled. This can happen if the network does not currently
-     * support the capability that is enabled. A capability that is disabled by the framework (via
-     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
-     */
-    protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
-        super.notifyCapabilitiesStatusChanged(c);
-    }
-
-    /**
-     * Notify the framework of an incoming call.
-     * @param c The {@link ImsCallSession} of the new incoming call.
-     *
-     * @throws RemoteException if the connection to the framework is not available. If this happens,
-     *     the call should be no longer considered active and should be cleaned up.
-     * */
-    protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            mListener.onIncomingCall(c.getSession());
-        }
-    }
-
-    /**
-     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
-     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
-     * includes a capability A to enable or disable, this method should return the correct enabled
-     * status for capability A.
-     * @param capability The capability that we are querying the configuration for.
-     * @return true if the capability is enabled, false otherwise.
-     */
-    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
-            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
-        // Base implementation - Override to provide functionality
-        return false;
-    }
-
-    /**
-     * The MmTelFeature should override this method to handle the enabling/disabling of
-     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
-     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
-     * could not be set to their new values,
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
-     * individually for each capability whose processing resulted in an error.
-     *
-     * Enabling/Disabling a capability here indicates that the capability should be registered or
-     * deregistered (depending on the capability change) and become available or unavailable to
-     * the framework.
-     */
-    @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Base implementation, no-op
-    }
-
-    /**
-     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
-     *
-     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
-     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
-     * @param callType a call type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
-     * @return a {@link ImsCallProfile} object
-     */
-    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * Creates an {@link ImsCallSession} with the specified call profile.
-     * Use other methods, if applicable, instead of interacting with
-     * {@link ImsCallSession} directly.
-     *
-     * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
-     */
-    public ImsCallSession createCallSession(ImsCallProfile profile,
-            ImsCallSessionListener listener) {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Ut interface for the supplementary service configuration.
-     */
-    public ImsUtImplBase getUt() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsEcbmImplBase getEcbm() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsMultiEndpointImplBase getMultiEndpoint() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * Sets the current UI TTY mode for the MmTelFeature.
-     * @param mode An integer containing the new UI TTY Mode, can consist of
-     *         {@link TelecomManager#TTY_MODE_OFF},
-     *         {@link TelecomManager#TTY_MODE_FULL},
-     *         {@link TelecomManager#TTY_MODE_HCO},
-     *         {@link TelecomManager#TTY_MODE_VCO}
-     * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
-     */
-    void setUiTtyMode(int mode, Message onCompleteMessage) {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureRemoved() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public final IImsMmTelFeature getBinder() {
-        return mImsMMTelBinder;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
deleted file mode 100644
index 33aec5d..0000000
--- a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
+++ /dev/null
@@ -1,173 +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.telephony.ims.internal.stub;
-
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
-
-import com.android.ims.ImsConfig;
-
-/**
- * Controls the modification of IMS specific configurations. For more information on the supported
- * IMS configuration constants, see {@link ImsConfig}.
- *
- * @hide
- */
-
-public class ImsConfigImplBase {
-
-    //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
-    // work first.
-    private final IImsConfig mBinder = new IImsConfig.Stub() {
-
-        @Override
-        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.addImsConfigCallback(c);
-        }
-
-        @Override
-        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.removeImsConfigCallback(c);
-        }
-
-        @Override
-        public int getConfigInt(int item) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public String getConfigString(int item) throws RemoteException {
-            return null;
-        }
-
-        @Override
-        public int setConfigInt(int item, int value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public int setConfigString(int item, String value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-    };
-
-    public class Callback extends IImsConfigCallback.Stub {
-
-        @Override
-        public final void onIntConfigChanged(int item, int value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        @Override
-        public final void onStringConfigChanged(int item, String value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new integer value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, int value) {
-            // Base Implementation
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new String value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, String value) {
-            // Base Implementation
-        }
-    }
-
-    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
-
-    /**
-     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
-     * changes.
-     * @param c callback to add.
-     */
-    private void addImsConfigCallback(IImsConfigCallback c) {
-        mCallbacks.register(c);
-    }
-    /**
-     * Removes a {@link Callback} to the list of callbacks notified when a value in the
-     * configuration changes.
-     *
-     * @param c callback to remove.
-     */
-    private void removeImsConfigCallback(IImsConfigCallback c) {
-        mCallbacks.unregister(c);
-    }
-
-    public final IImsConfig getBinder() {
-        return mBinder;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setConfig(int item, int value) {
-        // Base Implementation - To be overridden.
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setConfig(int item, String value) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in Integer format.
-     */
-    public int getConfigInt(int item) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in String format.
-     */
-    public String getConfigString(int item) {
-        return null;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index 80b2f78..c6ca6fd 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,105 +16,259 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallSession;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsVideoCallProvider;
+import android.telephony.ims.ImsVideoCallProvider;
+
+import dalvik.system.CloseGuard;
 
 /**
- * Base implementation of IImsCallSession, which implements stub versions of the methods in the
- * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
  *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSession maintained by other ImsServices.
+ * Override the methods that your implementation of ImsCallSession supports.
  *
  * @hide
  */
-
-public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+@SystemApi
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsCallSession maintained by other ImsServices.
+public class ImsCallSessionImplBase implements AutoCloseable {
+    /**
+     * Notify USSD Mode.
+     */
+    public static final int USSD_MODE_NOTIFY = 0;
+    /**
+     * Request USSD Mode
+     */
+    public static final int USSD_MODE_REQUEST = 1;
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Defines IMS call session state.
      */
-    @Override
-    public void close() throws RemoteException {
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
 
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        /**
+         * @hide
+         */
+        private State() {
+        }
     }
 
-    /**
-     * Gets the call ID of the session.
-     *
-     * @return the call ID
-     */
-    @Override
-    public String getCallId() throws RemoteException {
-        return null;
-    }
+    // Non-final for injection by tests
+    private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+        @Override
+        public void close() {
+            ImsCallSessionImplBase.this.close();
+        }
+
+        @Override
+        public String getCallId() {
+            return ImsCallSessionImplBase.this.getCallId();
+        }
+
+        @Override
+        public ImsCallProfile getCallProfile() {
+            return ImsCallSessionImplBase.this.getCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getLocalCallProfile() {
+            return ImsCallSessionImplBase.this.getLocalCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getRemoteCallProfile() {
+            return ImsCallSessionImplBase.this.getRemoteCallProfile();
+        }
+
+        @Override
+        public String getProperty(String name) {
+            return ImsCallSessionImplBase.this.getProperty(name);
+        }
+
+        @Override
+        public int getState() {
+            return ImsCallSessionImplBase.this.getState();
+        }
+
+        @Override
+        public boolean isInCall() {
+            return ImsCallSessionImplBase.this.isInCall();
+        }
+
+        @Override
+        public void setListener(IImsCallSessionListener listener) {
+            ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+        }
+
+        @Override
+        public void setMute(boolean muted) {
+            ImsCallSessionImplBase.this.setMute(muted);
+        }
+
+        @Override
+        public void start(String callee, ImsCallProfile profile) {
+            ImsCallSessionImplBase.this.start(callee, profile);
+        }
+
+        @Override
+        public void startConference(String[] participants, ImsCallProfile profile) throws
+                RemoteException {
+            ImsCallSessionImplBase.this.startConference(participants, profile);
+        }
+
+        @Override
+        public void accept(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.accept(callType, profile);
+        }
+
+        @Override
+        public void reject(int reason) {
+            ImsCallSessionImplBase.this.reject(reason);
+        }
+
+        @Override
+        public void terminate(int reason) {
+            ImsCallSessionImplBase.this.terminate(reason);
+        }
+
+        @Override
+        public void hold(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.hold(profile);
+        }
+
+        @Override
+        public void resume(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.resume(profile);
+        }
+
+        @Override
+        public void merge() {
+            ImsCallSessionImplBase.this.merge();
+        }
+
+        @Override
+        public void update(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.update(callType, profile);
+        }
+
+        @Override
+        public void extendToConference(String[] participants) {
+            ImsCallSessionImplBase.this.extendToConference(participants);
+        }
+
+        @Override
+        public void inviteParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.inviteParticipants(participants);
+        }
+
+        @Override
+        public void removeParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.removeParticipants(participants);
+        }
+
+        @Override
+        public void sendDtmf(char c, Message result) {
+            ImsCallSessionImplBase.this.sendDtmf(c, result);
+        }
+
+        @Override
+        public void startDtmf(char c) {
+            ImsCallSessionImplBase.this.startDtmf(c);
+        }
+
+        @Override
+        public void stopDtmf() {
+            ImsCallSessionImplBase.this.stopDtmf();
+        }
+
+        @Override
+        public void sendUssd(String ussdMessage) {
+            ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+        }
+
+        @Override
+        public IImsVideoCallProvider getVideoCallProvider() {
+            return ImsCallSessionImplBase.this.getVideoCallProvider();
+        }
+
+        @Override
+        public boolean isMultiparty() {
+            return ImsCallSessionImplBase.this.isMultiparty();
+        }
+
+        @Override
+        public void sendRttModifyRequest(ImsCallProfile toProfile) {
+            ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+        }
+
+        @Override
+        public void sendRttModifyResponse(boolean status) {
+            ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+        }
+
+        @Override
+        public void sendRttMessage(String rttMessage) {
+            ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+        }
+    };
 
     /**
-     * Gets the call profile that this session is associated with
-     *
-     * @return the {@link ImsCallProfile} that this session is associated with
+     * @hide
      */
-    @Override
-    public ImsCallProfile getCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the local call profile that this session is associated with
-     *
-     * @return the local {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getLocalCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the remote call profile that this session is associated with
-     *
-     * @return the remote {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getRemoteCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the value associated with the specified property of this session.
-     *
-     * @return the string value associated with the specified property
-     */
-    @Override
-    public String getProperty(String name) throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the session state.
-     * The value returned must be one of the states in {@link ImsCallSession.State}.
-     *
-     * @return the session state
-     */
-    @Override
-    public int getState() throws RemoteException {
-        return ImsCallSession.State.INVALID;
-    }
-
-    /**
-     * Checks if the session is in call.
-     *
-     * @return true if the session is in call, false otherwise
-     */
-    @Override
-    public boolean isInCall() throws RemoteException {
-        return false;
+    public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+        setListener(new ImsCallSessionListener(listener));
     }
 
     /**
@@ -122,25 +276,87 @@
      * can only hold one listener at a time. Subsequent calls to this method
      * override the previous listener.
      *
-     * @param listener to listen to the session events of this object
+     * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
+     * to the ImsCallSession
+     */
+    public void setListener(ImsCallSessionListener listener) {
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
      */
     @Override
-    public void setListener(IImsCallSessionListener listener) throws RemoteException {
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state, defined in
+     * {@link ImsCallSessionImplBase.State}.
+     */
+    public int getState() {
+        return ImsCallSessionImplBase.State.INVALID;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    public boolean isInCall() {
+        return false;
     }
 
     /**
      * Mutes or unmutes the mic for the active call.
      *
-     * @param muted true if the call is muted, false otherwise
+     * @param muted true if the call should be muted, false otherwise.
      */
-    @Override
-    public void setMute(boolean muted) throws RemoteException {
+    public void setMute(boolean muted) {
     }
 
     /**
-     * Initiates an IMS call with the specified target and call profile.
-     * The session listener set in {@link #setListener} is called back upon defined session events.
-     * The method is only valid to call when the session state is in
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
      * @param callee dialed string to make the call to
@@ -149,13 +365,13 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void start(String callee, ImsCallProfile profile) throws RemoteException {
+    public void start(String callee, ImsCallProfile profile) {
     }
 
     /**
      * Initiates an IMS call with the specified participants and call profile.
-     * The session listener set in {@link #setListener} is called back upon defined session events.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
      * The method is only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
@@ -165,9 +381,7 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void startConference(String[] participants, ImsCallProfile profile)
-            throws RemoteException {
+    public void startConference(String[] participants, ImsCallProfile profile) {
     }
 
     /**
@@ -177,31 +391,26 @@
      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
      * @see {@link ImsCallSession.Listener#callSessionStarted}
      */
-    @Override
-    public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    public void accept(int callType, ImsStreamMediaProfile profile) {
     }
 
     /**
      * Rejects an incoming call or session update.
      *
-     * @param reason reason code to reject an incoming call, defined in
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void reject(int reason) throws RemoteException {
+    public void reject(int reason) {
     }
 
     /**
      * Terminates a call.
      *
-     * @param reason reason code to terminate a call, defined in
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
      *
      * @see {@link ImsCallSession.Listener#callSessionTerminated}
      */
-    @Override
-    public void terminate(int reason) throws RemoteException {
+    public void terminate(int reason) {
     }
 
     /**
@@ -212,8 +421,7 @@
      * @see {@link ImsCallSession.Listener#callSessionHeld},
      * {@link ImsCallSession.Listener#callSessionHoldFailed}
      */
-    @Override
-    public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+    public void hold(ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -224,12 +432,11 @@
      * @see {@link ImsCallSession.Listener#callSessionResumed},
      * {@link ImsCallSession.Listener#callSessionResumeFailed}
      */
-    @Override
-    public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+    public void resume(ImsStreamMediaProfile profile) {
     }
 
     /**
-     * Merges the active & hold call. When the merge starts,
+     * Merges the active and held call. When the merge starts,
      * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
      * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
      * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
@@ -239,8 +446,7 @@
      * {@link ImsCallSession.Listener#callSessionMergeComplete},
      *      {@link ImsCallSession.Listener#callSessionMergeFailed}
      */
-    @Override
-    public void merge() throws RemoteException {
+    public void merge() {
     }
 
     /**
@@ -251,8 +457,7 @@
      * @see {@link ImsCallSession.Listener#callSessionUpdated},
      * {@link ImsCallSession.Listener#callSessionUpdateFailed}
      */
-    @Override
-    public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    public void update(int callType, ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -263,8 +468,7 @@
      * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
      * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
      */
-    @Override
-    public void extendToConference(String[] participants) throws RemoteException {
+    public void extendToConference(String[] participants) {
     }
 
     /**
@@ -274,8 +478,7 @@
      * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
      */
-    @Override
-    public void inviteParticipants(String[] participants) throws RemoteException {
+    public void inviteParticipants(String[] participants) {
     }
 
     /**
@@ -285,8 +488,7 @@
      * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
      */
-    @Override
-    public void removeParticipants(String[] participants) throws RemoteException {
+    public void removeParticipants(String[] participants) {
     }
 
     /**
@@ -296,8 +498,7 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void sendDtmf(char c, Message result) throws RemoteException {
+    public void sendDtmf(char c, Message result) {
     }
 
     /**
@@ -307,15 +508,13 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void startDtmf(char c) throws RemoteException {
+    public void startDtmf(char c) {
     }
 
     /**
      * Stop a DTMF code.
      */
-    @Override
-    public void stopDtmf() throws RemoteException {
+    public void stopDtmf() {
     }
 
     /**
@@ -323,17 +522,23 @@
      *
      * @param ussdMessage USSD message to send
      */
-    @Override
-    public void sendUssd(String ussdMessage) throws RemoteException {
+    public void sendUssd(String ussdMessage) {
     }
 
     /**
-     * Returns a binder for the video call provider implementation contained within the IMS service
-     * process. This binder is used by the VideoCallProvider subclass in Telephony which
-     * intermediates between the propriety implementation and Telecomm/InCall.
+     * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+     * @hide
      */
-    @Override
-    public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+    public IImsVideoCallProvider getVideoCallProvider() {
+        ImsVideoCallProvider provider = getImsVideoCallProvider();
+        return provider != null ? provider.getInterface() : null;
+    }
+
+    /**
+     * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
+     * process.
+     */
+    public ImsVideoCallProvider getImsVideoCallProvider() {
         return null;
     }
 
@@ -341,8 +546,7 @@
      * Determines if the current session is multiparty.
      * @return {@code True} if the session is multiparty.
      */
-    @Override
-    public boolean isMultiparty() throws RemoteException {
+    public boolean isMultiparty() {
         return false;
     }
 
@@ -350,16 +554,13 @@
      * Device issues RTT modify request
      * @param toProfile The profile with requested changes made
      */
-    @Override
     public void sendRttModifyRequest(ImsCallProfile toProfile) {
     }
 
     /**
      * Device responds to Remote RTT modify request
-     * @param status true  Accepted the request
-     *                false  Declined the request
+     * @param status true if the the request was accepted or false of the request is defined.
      */
-    @Override
     public void sendRttModifyResponse(boolean status) {
     }
 
@@ -367,7 +568,16 @@
      * Device sends RTT message
      * @param rttMessage RTT message to be sent
      */
-    @Override
     public void sendRttMessage(String rttMessage) {
     }
+
+    /** @hide */
+    public IImsCallSession getServiceImpl() {
+        return mServiceImpl;
+    }
+
+    /** @hide */
+    public void setServiceImpl(IImsCallSession serviceImpl) {
+        mServiceImpl = serviceImpl;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
deleted file mode 100644
index 6c18935..0000000
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
+++ /dev/null
@@ -1,298 +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.telephony.ims.stub;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-
-/**
- * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
- * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
- * ImsCallSessionListener supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
-    /**
-     * Notifies the result of the basic session operation (setup / terminate).
-     */
-    @Override
-    public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the call hold/resume operation.
-     */
-    @Override
-    public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call merge operation.
-     */
-    @Override
-    public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeComplete(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call upgrade / downgrade or any other call
-     * updates.
-     */
-    @Override
-    public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of conference extension.
-     */
-    @Override
-    public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendReceived(IImsCallSession session,
-            IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the participant invitation / removal to/from the
-     * conference session.
-     */
-    @Override
-    public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the changes of the conference info. the conference session.
-     */
-    @Override
-    public void callSessionConferenceStateUpdated(IImsCallSession session,
-            ImsConferenceState state) {
-        // no-op
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    @Override
-    public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
-            String ussdMessage) {
-        // no-op
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
-     * handover from one radio technology to another.
-     * @param session
-     * @param srcAccessTech The source radio access technology; one of the access technology
-     *                      constants defined in {@link android.telephony.ServiceState}.  For
-     *                      example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     * @param targetAccessTech The target radio access technology; one of the access technology
-     *                      constants defined in {@link android.telephony.ServiceState}.  For
-     *                      example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     */
-    @Override
-    public void callSessionMayHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech) {
-        // no-op
-    }
-
-    /**
-     * Notifies of handover information for this call
-     */
-    @Override
-    public void callSessionHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the TTY mode change by remote party.
-     *
-     * @param mode one of the following: -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
-     */
-    @Override
-    public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
-        // no-op
-    }
-
-    /**
-     * Notifies of a change to the multiparty state for this
-     * {@code ImsCallSession}.
-     *
-     * @param session The call session.
-     * @param isMultiParty {@code true} if the session became multiparty,
-     *            {@code false} otherwise.
-     */
-    @Override
-    public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
-        // no-op
-    }
-
-    /**
-     * Notifies the supplementary service information for the current session.
-     */
-    @Override
-    public void callSessionSuppServiceReceived(IImsCallSession session,
-            ImsSuppServiceNotification suppSrvNotification) {
-        // no-op
-    }
-
-    /**
-     * Received RTT modify request from Remote Party
-     * @param session The call session.
-     * @param callProfile ImsCallProfile with updated attribute
-     */
-    @Override
-    public void callSessionRttModifyRequestReceived(IImsCallSession session,
-            ImsCallProfile callProfile) {
-        // no-op
-    }
-
-    /**
-     * Received response for RTT modify request
-     * @param status true : Accepted the request
-     *               false : Declined the request
-     */
-    @Override
-    public void callSessionRttModifyResponseReceived(int status) {
-        // no -op
-    }
-
-    /**
-     * Device received RTT message from Remote UE
-     * @param rttMessage RTT message received
-     */
-    @Override
-    public void callSessionRttMessageReceived(String rttMessage) {
-        // no-op
-    }
-}
-
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 1670e6b..dcd7ea7 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -16,31 +16,24 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.util.Log;
 
 import com.android.ims.ImsConfig;
-import com.android.ims.ImsConfigListener;
-import com.android.ims.internal.IImsConfig;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 
-
 /**
- * Base implementation of ImsConfig.
- * Override the methods that your implementation of ImsConfig supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsConfig maintained by other ImsServices.
- *
- * Provides APIs to get/set the IMS service feature/capability/parameters.
- * The config items include:
- * 1) Items provisioned by the operator.
- * 2) Items configured by user. Mainly service feature class.
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
  *
  * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
  * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
@@ -51,142 +44,10 @@
  * performed every time.
  * @hide
  */
-
+@SystemApi
 public class ImsConfigImplBase {
 
-    static final private String TAG = "ImsConfigImplBase";
-
-    ImsConfigStub mImsConfigStub;
-
-    public ImsConfigImplBase(Context context) {
-        mImsConfigStub = new ImsConfigStub(this, context);
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in Integer format.
-     */
-    public int getProvisionedValue(int item) throws RemoteException {
-        return -1;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in String format.
-     */
-    public String getProvisionedStringValue(int item) throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setProvisionedValue(int item, int value) throws RemoteException {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.  Synchronous blocking call.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setProvisionedStringValue(int item, String value) throws RemoteException {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value of the specified IMS feature item for specified network type.
-     * This operation gets the feature config value from the master storage (i.e. final
-     * value). Asynchronous non-blocking call.
-     *
-     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
-     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param listener feature value returned asynchronously through listener.
-     */
-    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
-            throws RemoteException {
-    }
-
-    /**
-     * Sets the value for IMS feature item for specified network type.
-     * This operation stores the user setting in setting db from which master db
-     * is derived.
-     *
-     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
-     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
-     * @param listener, provided if caller needs to be notified for set result.
-     */
-    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
-            throws RemoteException {
-    }
-
-    /**
-     * Gets the value for IMS VoLTE provisioned.
-     * This should be the same as the operator provisioned value if applies.
-     */
-    public boolean getVolteProvisioned() throws RemoteException {
-        return false;
-    }
-
-    /**
-     * Gets the value for IMS feature item video quality.
-     *
-     * @param listener Video quality value returned asynchronously through listener.
-     */
-    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
-    }
-
-    /**
-     * Sets the value for IMS feature item video quality.
-     *
-     * @param quality, defines the value of video quality.
-     * @param listener, provided if caller needs to be notified for set result.
-     */
-    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
-    }
-
-    public IImsConfig getIImsConfig() { return mImsConfigStub; }
-
-    /**
-     * Updates provisioning value and notifies the framework of the change.
-     * Doesn't call #setProvisionedValue and assumes the result succeeded.
-     * This should only be used by modem when they implicitly changed provisioned values.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     */
-    public final void notifyProvisionedValueChanged(int item, int value) {
-        mImsConfigStub.updateCachedValue(item, value, true);
-    }
-
-    /**
-     * Updates provisioning value and notifies the framework of the change.
-     * Doesn't call #setProvisionedValue and assumes the result succeeded.
-     * This should only be used by modem when they implicitly changed provisioned values.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     */
-    public final void notifyProvisionedValueChanged(int item, String value) {
-        mImsConfigStub.updateCachedValue(item, value, true);
-    }
+    private static final String TAG = "ImsConfigImplBase";
 
     /**
      * Implements the IImsConfig AIDL interface, which is called by potentially many processes
@@ -208,32 +69,41 @@
      */
     @VisibleForTesting
     static public class ImsConfigStub extends IImsConfig.Stub {
-        Context mContext;
         WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
         private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
         private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
 
         @VisibleForTesting
-        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
-            mContext = context;
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
             mImsConfigImplBaseWeakReference =
                     new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
         }
 
+        @Override
+        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().addImsConfigCallback(c);
+        }
+
+        @Override
+        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().removeImsConfigCallback(c);
+        }
+
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call ImsConfigImplBase.getConfigInt.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-         * @return value in Integer format.
+         * @param item integer key
+         * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+         * unavailable.
          */
         @Override
-        public synchronized int getProvisionedValue(int item) throws RemoteException {
+        public synchronized int getConfigInt(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedIntValue.get(item);
             } else {
-                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                int retVal = getImsConfigImpl().getConfigInt(item);
                 if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -243,18 +113,18 @@
 
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call #ImsConfigImplBase.getConfigString.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @return value in String format.
          */
         @Override
-        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+        public synchronized String getConfigString(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedStringValue.get(item);
             } else {
-                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                String retVal = getImsConfigImpl().getConfigString(item);
                 if (retVal != null) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -268,16 +138,17 @@
          * from which the master value is derived, and write it into local cache.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @param value in Integer format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+        public synchronized int setConfigInt(int item, int value) throws RemoteException {
             mProvisionedIntValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                updateCachedValue(item, value, true);
             } else {
                 Log.d(TAG, "Set provision value of " + item +
                         " to " + value + " failed with error code " + retVal);
@@ -294,63 +165,21 @@
          *
          * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
          * @param value in String format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedStringValue(int item, String value)
+        public synchronized int setConfigString(int item, String value)
                 throws RemoteException {
             mProvisionedStringValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                updateCachedValue(item, value, true);
             }
 
             return retVal;
         }
 
-        /**
-         * Wrapper function to call ImsConfigImplBase.getFeatureValue.
-         */
-        @Override
-        public void getFeatureValue(int feature, int network, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().getFeatureValue(feature, network, listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.setFeatureValue.
-         */
-        @Override
-        public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().setFeatureValue(feature, network, value, listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
-         */
-        @Override
-        public boolean getVolteProvisioned() throws RemoteException {
-            return getImsConfigImpl().getVolteProvisioned();
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.getVideoQuality.
-         */
-        @Override
-        public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
-            getImsConfigImpl().getVideoQuality(listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.setVideoQuality.
-         */
-        @Override
-        public void setVideoQuality(int quality, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().setVideoQuality(quality, listener);
-        }
-
         private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
             ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
             if (ref == null) {
@@ -360,32 +189,228 @@
             }
         }
 
-        private void sendImsConfigChangedIntent(int item, int value) {
-            sendImsConfigChangedIntent(item, Integer.toString(value));
+        private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
         }
 
-        private void sendImsConfigChangedIntent(int item, String value) {
-            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
-            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
-            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
-            if (mContext != null) {
-                mContext.sendBroadcast(configChangedIntent);
-            }
+        private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
         }
 
-        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
+        throws RemoteException {
             mProvisionedIntValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
 
-        protected synchronized void updateCachedValue(
-                int item, String value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, String value,
+                boolean notifyChange) throws RemoteException {
             mProvisionedStringValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
     }
+
+    /**
+     * Callback that the framework uses for receiving Configuration change updates.
+     * {@hide}
+     */
+    public static class Callback extends IImsConfigCallback.Stub {
+
+        @Override
+        public final void onIntConfigChanged(int item, int value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        @Override
+        public final void onStringConfigChanged(int item, String value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new integer value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, String value) {
+            // Base Implementation
+        }
+    }
+
+    /**
+     * The configuration requested resulted in an unknown result. This may happen if the
+     * IMS configurations are unavailable.
+     */
+    public static final int CONFIG_RESULT_UNKNOWN = -1;
+    /**
+     * Setting the configuration value completed.
+     */
+    public static final int CONFIG_RESULT_SUCCESS = 0;
+    /**
+     * Setting the configuration value failed.
+     */
+    public static final int CONFIG_RESULT_FAILED =  1;
+
+    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+    ImsConfigStub mImsConfigStub;
+
+    /**
+     * Used for compatibility between older versions of the ImsService.
+     * @hide
+     */
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    public ImsConfigImplBase() {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    /**
+     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
+     * changes.
+     * @param c callback to add.
+     */
+    private void addImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.register(c);
+    }
+    /**
+     * Removes a {@link Callback} to the list of callbacks notified when a value in the
+     * configuration changes.
+     *
+     * @param c callback to remove.
+     */
+    private void removeImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.unregister(c);
+    }
+
+    /**
+     * @param item
+     * @param value
+     */
+    private final void notifyConfigChanged(int item, int value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        mCallbacks.broadcast(c -> {
+            try {
+                c.onIntConfigChanged(item, value);
+            } catch (RemoteException e) {
+                Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+            }
+        });
+    }
+
+    private void notifyConfigChanged(int item, String value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        mCallbacks.broadcast(c -> {
+            try {
+                c.onStringConfigChanged(item, value);
+            } catch (RemoteException e) {
+                Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+            }
+        });
+    }
+
+    /**
+     * @hide
+     */
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        try {
+            mImsConfigStub.updateCachedValue(item, value, true);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        try {
+        mImsConfigStub.updateCachedValue(item, value, true);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value an integer containing the configuration value.
+     * @return the result of setting the configuration value, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public int setConfig(int item, int value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value a String containing the new configuration value.
+     * @return Result of setting the configuration value, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public int setConfig(int item, String value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+     * unavailable.
+     */
+    public int getConfigInt(int item) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_UNKNOWN;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in String format or {@code null} if unavailable.
+     */
+    public String getConfigString(int item) {
+        // Base Implementation - To be overridden.
+        return null;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 89f95ff..06c35ea 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,7 +16,9 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsEcbmListener;
@@ -30,22 +32,65 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsEcbmImplBase {
+    private static final String TAG = "ImsEcbmImplBase";
 
-public class ImsEcbmImplBase extends IImsEcbm.Stub {
+    private IImsEcbmListener mListener;
+    private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+        @Override
+        public void setListener(IImsEcbmListener listener) {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsEcbmListener listener) throws RemoteException {
+        @Override
+        public void exitEmergencyCallbackMode() {
+            ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+        }
+    };
 
+    /** @hide */
+    public IImsEcbm getImsEcbm() {
+        return mImsEcbm;
     }
 
     /**
-     * Requests Modem to come out of ECBM mode
+     * This method should be implemented by the IMS provider. Framework will trigger this method to
+     * request to come out of ECBM mode
      */
-    @Override
-    public void exitEmergencyCallbackMode() throws RemoteException {
+    public void exitEmergencyCallbackMode() {
+        Log.d(TAG, "exitEmergencyCallbackMode() not implemented");
+    }
 
+    /**
+     * Notifies the framework when the device enters Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void enteredEcbm() {
+        Log.d(TAG, "Entered ECBM.");
+        if (mListener != null) {
+            try {
+                mListener.enteredECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the framework when the device exits Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void exitedEcbm() {
+        Log.d(TAG, "Exited ECBM.");
+        if (mListener != null) {
+            try {
+                mListener.exitedECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
similarity index 93%
rename from telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
rename to telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
index e890cf8..e2ae0e8 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
@@ -14,6 +14,6 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
 parcelable ImsFeatureConfiguration;
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
similarity index 77%
rename from telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
rename to telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 244c957..98b67c3 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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,29 +14,34 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.feature.ImsFeature;
 import android.util.ArraySet;
 
-import java.util.Arrays;
 import java.util.Set;
 
 /**
  * Container class for IMS Feature configuration. This class contains the features that the
- * ImsService supports, which are defined in {@link ImsFeature.FeatureType}.
+ * ImsService supports, which are defined in {@link ImsFeature} as
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ *
  * @hide
  */
-public class ImsFeatureConfiguration implements Parcelable {
+@SystemApi
+public final class ImsFeatureConfiguration implements Parcelable {
     /**
      * Features that this ImsService supports.
      */
     private final Set<Integer> mFeatures;
 
     /**
-     * Creates an ImsFeatureConfiguration with the features
+     * Builder for {@link ImsFeatureConfiguration} that makes adding supported {@link ImsFeature}s
+     * easier.
      */
     public static class Builder {
             ImsFeatureConfiguration mConfig;
@@ -72,7 +77,10 @@
      * Configuration of the ImsService, which describes which features the ImsService supports
      * (for registration).
      * @param features an array of feature integers defined in {@link ImsFeature} that describe
-     * which features this ImsService supports.
+     * which features this ImsService supports. Supported values are
+     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+     * {@link ImsFeature#FEATURE_RCS}.
+     * @hide
      */
     public ImsFeatureConfiguration(int[] features) {
         mFeatures = new ArraySet<>();
@@ -85,7 +93,9 @@
     }
 
     /**
-     * @return an int[] containing the features that this ImsService supports.
+     * @return an int[] containing the features that this ImsService supports. Supported values are
+     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+     * {@link ImsFeature#FEATURE_RCS}.
      */
     public int[] getServiceFeatures() {
         return mFeatures.stream().mapToInt(i->i).toArray();
@@ -95,6 +105,7 @@
         mFeatures.add(feature);
     }
 
+    /** @hide */
     protected ImsFeatureConfiguration(Parcel in) {
         int[] features = in.createIntArray();
         if (features != null) {
@@ -130,16 +141,23 @@
         dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof ImsFeatureConfiguration)) return false;
 
-        ImsFeatureConfiguration that = (ImsFeatureConfiguration) o;
+        ImsFeatureConfiguration
+                that = (ImsFeatureConfiguration) o;
 
         return mFeatures.equals(that.mFeatures);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int hashCode() {
         return mFeatures.hashCode();
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 05da9da..ce2d89a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,11 +16,16 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
+import android.telephony.ims.ImsExternalCallState;
 import com.android.ims.internal.IImsExternalCallStateListener;
 import com.android.ims.internal.IImsMultiEndpoint;
 
+import java.util.List;
+
 /**
  * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
  * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
@@ -31,23 +36,49 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsMultiEndpointImplBase {
+    private static final String TAG = "MultiEndpointImplBase";
 
-public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+    private IImsExternalCallStateListener mListener;
+    private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+        @Override
+        public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+        @Override
+        public void requestImsExternalCallStateInfo() throws RemoteException {
+            ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+        }
+    };
 
+    /** @hide */
+    public IImsMultiEndpoint getIImsMultiEndpoint() {
+        return mImsMultiEndpoint;
     }
 
     /**
-     * Query API to get the latest Dialog Event Package information
-     * Should be invoked only after setListener is done
+     * Notifies framework when Dialog Event Package update is received
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
      */
-    @Override
-    public void requestImsExternalCallStateInfo() throws RemoteException {
+    public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+        Log.d(TAG, "ims external call state update triggered.");
+        if (mListener != null) {
+            try {
+                mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
 
+    /**
+     * This method should be implemented by the IMS provider. Framework will trigger this to get the
+     * latest Dialog Event Package information. Should
+     */
+    public void requestImsExternalCallStateInfo() {
+        Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 42af083..4334d3a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,15 +17,16 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.net.Uri;
-import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationCallback;
+import android.telephony.ims.ImsReasonInfo;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -36,11 +37,14 @@
  * registration for this ImsService has changed status.
  * @hide
  */
-
+@SystemApi
 public class ImsRegistrationImplBase {
 
     private static final String LOG_TAG = "ImsRegistrationImplBase";
 
+    /**
+     * @hide
+     */
     // Defines the underlying radio technology type that we have registered for IMS over.
     @IntDef(flag = true,
             value = {
@@ -155,6 +159,9 @@
     // Locked on mLock, create unspecified disconnect cause.
     private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
 
+    /**
+     * @hide
+     */
     public final IImsRegistration getBinder() {
         return mBinder;
     }
@@ -171,8 +178,8 @@
     /**
      * Notify the framework that the device is connected to the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
@@ -189,8 +196,8 @@
     /**
      * Notify the framework that the device is trying to connect the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
@@ -221,6 +228,13 @@
         });
     }
 
+    /**
+     * Notify the framework that the handover from the current radio technology to the technology
+     * defined in {@code imsRadioTech} has failed.
+     * @param imsRadioTech The technology that has failed to be changed. Valid values are
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+     * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     */
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         mCallbacks.broadcast((c) -> {
@@ -233,6 +247,11 @@
         });
     }
 
+    /**
+     * The this device's subscriber associated {@link Uri}s have changed, which are used to filter
+     * out this device's {@link Uri}s during conference calling.
+     * @param uris
+     */
     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
         mCallbacks.broadcast((c) -> {
             try {
@@ -264,6 +283,11 @@
         }
     }
 
+    /**
+     * @return the current registration connection type. Valid values are
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
+     * @hide
+     */
     @VisibleForTesting
     public final @ImsRegistrationTech int getConnectionType() {
         synchronized (mLock) {
diff --git a/telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
similarity index 97%
rename from telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java
rename to telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 89acc80..0673a38 100644
--- a/telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -14,17 +14,16 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
+import android.telephony.ims.aidl.IImsSmsListener;
 import android.util.Log;
 
-import com.android.ims.internal.IImsSmsListener;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -37,7 +36,7 @@
  * @hide
  */
 @SystemApi
-public class SmsImplBase {
+public class ImsSmsImplBase {
     private static final String LOG_TAG = "SmsImplBase";
 
     /** @hide */
@@ -158,7 +157,7 @@
      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
      * @param result result of delivering the message. Valid values are:
      *  {@link #DELIVER_STATUS_OK},
-     *  {@link #DELIVER_STATUS_OK}
+     *  {@link #DELIVER_STATUS_ERROR}
      * @param messageRef the message reference
      */
     public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
@@ -299,9 +298,9 @@
     }
 
     /**
-     * Called when SmsImpl has been initialized and communication with the framework is set up.
+     * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
      * Any attempt by this class to access the framework before this method is called will return
-     * with an {@link RuntimeException}.
+     * with a {@link RuntimeException}.
      */
     public void onReady() {
         // Base Implementation - Should be overridden
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 054a8b2..fcd7faf 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -16,177 +16,332 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
 
 /**
- * Base implementation of ImsUt, which implements stub versions of the methods
- * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsUt maintained by other ImsServices.
- *
- * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ * Base implementation of IMS UT interface, which implements stubs. Override these methods to
+ * implement functionality.
  *
  * @hide
  */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtImplBase {
 
-public class ImsUtImplBase extends IImsUt.Stub {
+    private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+        @Override
+        public void close() throws RemoteException {
+            ImsUtImplBase.this.close();
+        }
+
+        @Override
+        public int queryCallBarring(int cbType) throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarring(cbType);
+        }
+
+        @Override
+        public int queryCallForward(int condition, String number) throws RemoteException {
+            return ImsUtImplBase.this.queryCallForward(condition, number);
+        }
+
+        @Override
+        public int queryCallWaiting() throws RemoteException {
+            return ImsUtImplBase.this.queryCallWaiting();
+        }
+
+        @Override
+        public int queryCLIR() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIR();
+        }
+
+        @Override
+        public int queryCLIP() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIP();
+        }
+
+        @Override
+        public int queryCOLR() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLR();
+        }
+
+        @Override
+        public int queryCOLP() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLP();
+        }
+
+        @Override
+        public int transact(Bundle ssInfo) throws RemoteException {
+            return ImsUtImplBase.this.transact(ssInfo);
+        }
+
+        @Override
+        public int updateCallBarring(int cbType, int action, String[] barrList) throws
+                RemoteException {
+            return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+        }
+
+        @Override
+        public int updateCallForward(int action, int condition, String number, int serviceClass,
+                int timeSeconds) throws RemoteException {
+            return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
+                    timeSeconds);
+        }
+
+        @Override
+        public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+        }
+
+        @Override
+        public int updateCLIR(int clirMode) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIR(clirMode);
+        }
+
+        @Override
+        public int updateCLIP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIP(enable);
+        }
+
+        @Override
+        public int updateCOLR(int presentation) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLR(presentation);
+        }
+
+        @Override
+        public int updateCOLP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLP(enable);
+        }
+
+        @Override
+        public void setListener(IImsUtListener listener) throws RemoteException {
+            ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+        }
+
+        @Override
+        public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+                throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+        }
+
+        @Override
+        public int updateCallBarringForServiceClass(int cbType, int action,
+                String[] barrList, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallBarringForServiceClass(
+                    cbType, action, barrList, serviceClass);
+        }
+    };
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Called when the framework no longer needs to interact with the IMS UT implementation any
+     * longer.
      */
-    @Override
-    public void close() throws RemoteException {
+    public void close() {
 
     }
 
     /**
-     * Retrieves the configuration of the call barring.
+     * Retrieves the call barring configuration.
+     * @param cbType
      */
-    @Override
-    public int queryCallBarring(int cbType) throws RemoteException {
+    public int queryCallBarring(int cbType) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call barring for specified service class.
      */
-    @Override
-    public int queryCallBarringForServiceClass(int cbType, int serviceClass)
-            throws RemoteException {
+    public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call forward.
      */
-    @Override
-    public int queryCallForward(int condition, String number) throws RemoteException {
+    public int queryCallForward(int condition, String number) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call waiting.
      */
-    @Override
-    public int queryCallWaiting() throws RemoteException {
+    public int queryCallWaiting() {
         return -1;
     }
 
     /**
      * Retrieves the default CLIR setting.
+     * @hide
      */
-    @Override
-    public int queryCLIR() throws RemoteException {
+    public int queryCLIR() {
+        return queryClir();
+    }
+
+    /**
+     * Retrieves the CLIP call setting.
+     * @hide
+     */
+    public int queryCLIP() {
+        return queryClip();
+    }
+
+    /**
+     * Retrieves the COLR call setting.
+     * @hide
+     */
+    public int queryCOLR() {
+        return queryColr();
+    }
+
+    /**
+     * Retrieves the COLP call setting.
+     * @hide
+     */
+    public int queryCOLP() {
+        return queryColp();
+    }
+
+    /**
+     * Retrieves the default CLIR setting.
+     */
+    public int queryClir() {
         return -1;
     }
 
     /**
      * Retrieves the CLIP call setting.
      */
-    @Override
-    public int queryCLIP() throws RemoteException {
+    public int queryClip() {
         return -1;
     }
 
     /**
      * Retrieves the COLR call setting.
      */
-    @Override
-    public int queryCOLR() throws RemoteException {
+    public int queryColr() {
         return -1;
     }
 
     /**
      * Retrieves the COLP call setting.
      */
-    @Override
-    public int queryCOLP() throws RemoteException {
+    public int queryColp() {
         return -1;
     }
 
     /**
      * Updates or retrieves the supplementary service configuration.
      */
-    @Override
-    public int transact(Bundle ssInfo) throws RemoteException {
+    public int transact(Bundle ssInfo) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring.
      */
-    @Override
-    public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+    public int updateCallBarring(int cbType, int action, String[] barrList) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring for specified service class.
      */
-    @Override
     public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList,
-            int serviceClass) throws RemoteException {
+            int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call forward.
      */
-    @Override
     public int updateCallForward(int action, int condition, String number, int serviceClass,
-            int timeSeconds) throws RemoteException {
+            int timeSeconds) {
         return 0;
     }
 
     /**
      * Updates the configuration of the call waiting.
      */
-    @Override
-    public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+    public int updateCallWaiting(boolean enable, int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the CLIR supplementary service.
+     * @hide
      */
-    @Override
-    public int updateCLIR(int clirMode) throws RemoteException {
+    public int updateCLIR(int clirMode) {
+        return updateClir(clirMode);
+    }
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     * @hide
+     */
+    public int updateCLIP(boolean enable) {
+        return updateClip(enable);
+    }
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     * @hide
+     */
+    public int updateCOLR(int presentation) {
+        return updateColr(presentation);
+    }
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     * @hide
+     */
+    public int updateCOLP(boolean enable) {
+        return updateColp(enable);
+    }
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    public int updateClir(int clirMode) {
         return -1;
     }
 
     /**
      * Updates the configuration of the CLIP supplementary service.
      */
-    @Override
-    public int updateCLIP(boolean enable) throws RemoteException {
+    public int updateClip(boolean enable) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLR supplementary service.
      */
-    @Override
-    public int updateCOLR(int presentation) throws RemoteException {
+    public int updateColr(int presentation) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLP supplementary service.
      */
-    @Override
-    public int updateCOLP(boolean enable) throws RemoteException {
+    public int updateColp(boolean enable) {
         return -1;
     }
 
     /**
      * Sets the listener.
      */
-    @Override
-    public void setListener(IImsUtListener listener) throws RemoteException {
+    public void setListener(ImsUtListener listener) {
+    }
+
+    /**
+     * @hide
+     */
+    public IImsUt getInterface() {
+        return mServiceImpl;
     }
 }
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index cd0c4b1..1dda6bf 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -19,8 +19,9 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.telephony.Rlog;
-
-import com.android.ims.internal.IImsConfig;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.stub.ImsConfigImplBase;
 
 /**
  * Provides APIs to get/set the IMS service feature/capability/parameters.
@@ -46,7 +47,7 @@
 
     /**
      * Broadcast action: the configuration was changed
-     *
+     * @deprecated Use {@link ImsConfig#addConfigCallback(ImsConfigImplBase.Callback)} instead.
      * @hide
      */
     public static final String ACTION_IMS_CONFIG_CHANGED =
@@ -70,6 +71,8 @@
 
     /**
     * Defines IMS service/capability feature constants.
+    * @deprecated Use
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability} instead.
     */
     public static class FeatureConstants {
         public static final int FEATURE_TYPE_UNKNOWN = -1;
@@ -539,84 +542,133 @@
     }
 
     public ImsConfig(IImsConfig iconfig, Context context) {
-        if (DBG) Rlog.d(TAG, "ImsConfig creates");
+        if (DBG) Rlog.d(TAG, "ImsConfig created");
         miConfig = iconfig;
         mContext = context;
     }
 
     /**
-     * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
-     * This function should not be called from the mainthread as it could block the
-     * mainthread.
+     * @deprecated see {@link #getInt(int)} instead.
+     */
+    public int getProvisionedValue(int item) throws ImsException {
+        return getConfigInt(item);
+    }
+
+    /**
+     * Gets the configuration value for IMS service/capabilities parameters used by IMS stack.
      *
      * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
      * @return the value in Integer format.
-     *
-     * @throws ImsException if calling the IMS service results in an error.
+     * @throws ImsException if the ImsService is unavailable.
      */
-    public int getProvisionedValue(int item) throws ImsException {
+    public int getConfigInt(int item) throws ImsException {
         int ret = 0;
         try {
-            ret = miConfig.getProvisionedValue(item);
+            ret = miConfig.getConfigInt(item);
         }  catch (RemoteException e) {
-            throw new ImsException("getValue()", e,
+            throw new ImsException("getInt()", e,
                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
         }
-        if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret);
+        if (DBG) Rlog.d(TAG, "getInt(): item = " + item + ", ret =" + ret);
 
         return ret;
     }
 
     /**
-     * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
-     * This function should not be called from the mainthread as it could block the
-     * mainthread.
+     * @deprecated see {@link #getConfigString(int)} instead
+     */
+    public String getProvisionedStringValue(int item) throws ImsException {
+        return getConfigString(item);
+    }
+
+    /**
+     * Gets the configuration value for IMS service/capabilities parameters used by IMS stack.
      *
      * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
      * @return value in String format.
      *
-     * @throws ImsException if calling the IMS service results in an error.
+     * @throws ImsException if the ImsService is unavailable.
      */
-    public String getProvisionedStringValue(int item) throws ImsException {
+    public String getConfigString(int item) throws ImsException {
         String ret = "Unknown";
         try {
-            ret = miConfig.getProvisionedStringValue(item);
+            ret = miConfig.getConfigString(item);
         }  catch (RemoteException e) {
-            throw new ImsException("getProvisionedStringValue()", e,
+            throw new ImsException("getConfigString()", e,
                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
         }
-        if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret);
+        if (DBG) Rlog.d(TAG, "getConfigString(): item = " + item + ", ret =" + ret);
 
         return ret;
     }
 
     /**
-     * Sets the value for IMS service/capabilities parameters by
-     * the operator device management entity.
-     * This function should not be called from main thread as it could block
-     * mainthread.
+     * @deprecated see {@link #setConfig(int, int)} instead.
+     */
+    public int setProvisionedValue(int item, int value) throws ImsException {
+        return setConfig(item, value);
+    }
+
+    /**
+     * @deprecated see {@link #setConfig(int, String)} instead.
+     */
+    public int setProvisionedStringValue(int item, String value) throws ImsException {
+        return setConfig(item, value);
+    }
+
+    /**
+     * Sets the value for ImsService configuration item.
      *
      * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
      * @param value in Integer format.
      * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
      *
-     * @throws ImsException if calling the IMS service results in an error.
+     * @throws ImsException if the ImsService is unavailable.
      */
-    public int setProvisionedValue(int item, int value)
-            throws ImsException {
+    public int setConfig(int item, int value) throws ImsException {
         int ret = OperationStatusConstants.UNKNOWN;
         if (DBG) {
-            Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+            Rlog.d(TAG, "setConfig(): item = " + item +
                     "value = " + value);
         }
         try {
-            ret = miConfig.setProvisionedValue(item, value);
+            ret = miConfig.setConfigInt(item, value);
         }  catch (RemoteException e) {
-            throw new ImsException("setProvisionedValue()", e,
+            throw new ImsException("setConfig()", e,
                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
         }
         if (DBG) {
-            Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+            Rlog.d(TAG, "setConfig(): item = " + item +
+                    " value = " + value + " ret = " + ret);
+        }
+
+        return ret;
+
+    }
+
+    /**
+     * Sets the value for ImsService configuration item.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+     *
+     * @throws ImsException if the ImsService is unavailable.
+     */
+    public int setConfig(int item, String value) throws ImsException {
+        int ret = OperationStatusConstants.UNKNOWN;
+        if (DBG) {
+            Rlog.d(TAG, "setConfig(): item = " + item +
+                    "value = " + value);
+        }
+        try {
+            ret = miConfig.setConfigString(item, value);
+        }  catch (RemoteException e) {
+            throw new ImsException("setConfig()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+        if (DBG) {
+            Rlog.d(TAG, "setConfig(): item = " + item +
                     " value = " + value + " ret = " + ret);
         }
 
@@ -624,79 +676,32 @@
     }
 
     /**
-     * Sets the value for IMS service/capabilities parameters by
-     * the operator device management entity.
-     * This function should not be called from main thread as it could block
-     * mainthread.
+     * Adds a {@link ImsConfigImplBase.Callback} to the ImsService to notify when a Configuration
+     * item has changed.
      *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
-     *
-     * @throws ImsException if calling the IMS service results in an error.
+     * Make sure to call {@link #removeConfigCallback(ImsConfigImplBase.Callback)} when finished
+     * using this callback.
      */
-    public int setProvisionedStringValue(int item, String value)
-            throws ImsException {
-        int ret = OperationStatusConstants.UNKNOWN;
+    public void addConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+        if (DBG) Rlog.d(TAG, "addConfigCallback: " + callback);
         try {
-            ret = miConfig.setProvisionedStringValue(item, value);
+            miConfig.addImsConfigCallback(callback);
         }  catch (RemoteException e) {
-            throw new ImsException("setProvisionedStringValue()", e,
-                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
-        }
-        if (DBG) {
-            Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
-                    ", value =" + value);
-        }
-
-        return ret;
-    }
-
-    /**
-     * Gets the value for IMS feature item for specified network type.
-     *
-     * @param feature, defined as in FeatureConstants.
-     * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param listener, provided to be notified for the feature on/off status.
-     * @return void
-     *
-     * @throws ImsException if calling the IMS service results in an error.
-     */
-    public void getFeatureValue(int feature, int network,
-            ImsConfigListener listener) throws ImsException {
-        if (DBG) {
-            Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network +
-                    ", listener =" + listener);
-        }
-        try {
-            miConfig.getFeatureValue(feature, network, listener);
-        } catch (RemoteException e) {
-            throw new ImsException("getFeatureValue()", e,
+            throw new ImsException("addConfigCallback()", e,
                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
         }
     }
 
     /**
-     * Sets the value for IMS feature item for specified network type.
-     *
-     * @param feature, as defined in FeatureConstants.
-     * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param value, as defined in FeatureValueConstants.
-     * @param listener, provided if caller needs to be notified for set result.
-     * @return void
-     *
-     * @throws ImsException if calling the IMS service results in an error.
+     * Removes a {@link ImsConfigImplBase.Callback} from the ImsService that was previously added
+     * by {@link #addConfigCallback(ImsConfigImplBase.Callback)}.
      */
-    public void setFeatureValue(int feature, int network, int value,
-            ImsConfigListener listener) throws ImsException {
-        if (DBG) {
-            Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
-                    ", value =" + value + ", listener =" + listener);
-        }
+    public void removeConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+        if (DBG) Rlog.d(TAG, "removeConfigCallback: " + callback);
         try {
-            miConfig.setFeatureValue(feature, network, value, listener);
-        } catch (RemoteException e) {
-            throw new ImsException("setFeatureValue()", e,
+            miConfig.removeImsConfigCallback(callback);
+        }  catch (RemoteException e) {
+            throw new ImsException("removeConfigCallback()", e,
                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
         }
     }
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
index 0e8bad7..f35e886 100644
--- a/telephony/java/com/android/ims/ImsException.java
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -16,6 +16,8 @@
 
 package com.android.ims;
 
+import android.telephony.ims.ImsReasonInfo;
+
 /**
  * This class defines a general IMS-related exception.
  *
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 14c184a..c9d4405 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -18,6 +18,8 @@
 
 import android.os.Handler;
 import android.os.Message;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsSsInfo;
 
 /**
  * Provides APIs for the supplementary service settings using IMS (Ut interface).
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index c6fc5e5..203e6cf 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -17,9 +17,10 @@
 package com.android.ims.internal;
 
 import android.os.Message;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.IImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
 import com.android.ims.internal.IImsVideoCallProvider;
 
 /**
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 748092d..a8e8b7dd 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -16,12 +16,12 @@
 
 package com.android.ims.internal;
 
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsConferenceState;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ImsSuppServiceNotification;
 
 /**
  * A listener type for receiving notification on IMS call session events.
diff --git a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
index 1621967..b3d8139 100644
--- a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal;
 
-import com.android.ims.ImsExternalCallState;
+import android.telephony.ims.ImsExternalCallState;
 
 /**
  * A listener type for receiving notifications about DEP through IMS
diff --git a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
index 41b1042..b83b130 100644
--- a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
@@ -17,9 +17,9 @@
 package com.android.ims.internal;
 
 /**
-*  Interface from ImsFeature in the ImsService to ImsServiceController.
+ * Interface from ImsFeature in the ImsService to ImsServiceController.
  * {@hide}
  */
 oneway interface IImsFeatureStatusCallback {
     void notifyImsFeatureStatus(int featureStatus);
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
index 10c7f3e..5151192 100644
--- a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
+++ b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
@@ -18,14 +18,12 @@
 
 import android.app.PendingIntent;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsConfig;
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsSmsListener;
 import com.android.ims.internal.IImsUt;
 
 import android.os.Message;
@@ -44,8 +42,7 @@
     void addRegistrationListener(in IImsRegistrationListener listener);
     void removeRegistrationListener(in IImsRegistrationListener listener);
     ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType);
-    IImsCallSession createCallSession(int sessionId, in ImsCallProfile profile,
-            IImsCallSessionListener listener);
+    IImsCallSession createCallSession(int sessionId, in ImsCallProfile profile);
     IImsCallSession getPendingCallSession(int sessionId, String callId);
     IImsUt getUtInterface();
     IImsConfig getConfigInterface();
@@ -54,12 +51,4 @@
     IImsEcbm getEcbmInterface();
     void setUiTTYMode(int uiTtyMode, in Message onComplete);
     IImsMultiEndpoint getMultiEndpointInterface();
-    // SMS APIs
-    void setSmsListener(IImsSmsListener l);
-    oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
-            in byte[] pdu);
-    oneway void acknowledgeSms(int token, int messageRef, int result);
-    oneway void acknowledgeSmsReport(int token, int messageRef, int result);
-    String getSmsFormat();
-    oneway void onSmsReady();
 }
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 15f8726..2212109 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal;
 
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
 
 import android.net.Uri;
 
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index 406d22d..c3cc6fb 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -18,7 +18,7 @@
 
 import android.app.PendingIntent;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsConfig;
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 7ac25ac..857089f 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -18,7 +18,6 @@
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsRcsFeature;
 
 /**
@@ -30,5 +29,4 @@
     IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
     IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
-    IImsRegistration getRegistration(int slotId);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsUtListener.aidl b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
index 1bc0369..a603cd3 100644
--- a/telephony/java/com/android/ims/internal/IImsUtListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
@@ -18,11 +18,11 @@
 
 import android.os.Bundle;
 
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsSsInfo;
 import com.android.ims.internal.IImsUt;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
 
 /**
  * {@hide}
diff --git a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
index 80958c0..f323a0c 100644
--- a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
+++ b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /** The implementation of exponential backoff with jitter applied. */
 public class ExponentialBackoff {
     private int mRetryCounter;
@@ -27,8 +29,31 @@
     private long mMaximumDelayMs;
     private long mCurrentDelayMs;
     private int mMultiplier;
-    private Runnable mRunnable;
-    private Handler mHandler;
+    private final Runnable mRunnable;
+    private final Handler mHandler;
+
+    /**
+     * Implementation of Handler methods, Adapter for testing (can't spy on final methods).
+     */
+    private HandlerAdapter mHandlerAdapter = new HandlerAdapter() {
+        @Override
+        public boolean postDelayed(Runnable runnable, long delayMillis) {
+            return mHandler.postDelayed(runnable, delayMillis);
+        }
+
+        @Override
+        public void removeCallbacks(Runnable runnable) {
+            mHandler.removeCallbacks(runnable);
+        }
+    };
+
+    /**
+     * Need to spy final methods for testing.
+     */
+    public interface HandlerAdapter {
+        boolean postDelayed(Runnable runnable, long delayMillis);
+        void removeCallbacks(Runnable runnable);
+    }
 
     public ExponentialBackoff(
             long initialDelayMs,
@@ -57,14 +82,14 @@
     public void start() {
         mRetryCounter = 0;
         mCurrentDelayMs = mStartDelayMs;
-        mHandler.removeCallbacks(mRunnable);
-        mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+        mHandlerAdapter.removeCallbacks(mRunnable);
+        mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
     }
 
     /** Stops the backoff, all pending messages will be removed from the message queue. */
     public void stop() {
         mRetryCounter = 0;
-        mHandler.removeCallbacks(mRunnable);
+        mHandlerAdapter.removeCallbacks(mRunnable);
     }
 
     /** Should call when the retry action has failed and we want to retry after a longer delay. */
@@ -73,12 +98,17 @@
         long temp = Math.min(
                 mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
         mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
-        mHandler.removeCallbacks(mRunnable);
-        mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+        mHandlerAdapter.removeCallbacks(mRunnable);
+        mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
     }
 
     /** Returns the delay for the most recently posted message. */
     public long getCurrentDelay() {
         return mCurrentDelayMs;
     }
+
+    @VisibleForTesting
+    public void setHandlerAdapter(HandlerAdapter a) {
+        mHandlerAdapter  = a;
+    }
 }
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 dfb3c34..2b4c059 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,9 +38,10 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
@@ -680,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.
@@ -690,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);
 
     /**
@@ -787,20 +789,21 @@
     int getTetherApnRequired();
 
     /**
-     *  Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
-     *  as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
-     *  interface.
-     */
-    IImsMMTelFeature getMMTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+    * Enables framework IMS and triggers IMS Registration.
+    */
+    void enableIms(int slotId);
 
     /**
-     *  Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
-     *  as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
+    * Disables framework IMS and triggers IMS deregistration.
+    */
+    void disableIms(int slotId);
+
+    /**
+     *  Get IImsMmTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
+     *  as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
      *  interface.
-     *  Used for emergency calling only.
      */
-    IImsMMTelFeature getEmergencyMMTelFeatureAndListen(int slotId,
-            in IImsServiceFeatureCallback callback);
+    IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
 
     /**
      *  Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
@@ -815,6 +818,11 @@
     IImsRegistration getImsRegistration(int slotId, int feature);
 
     /**
+    * Returns the IImsConfig associated with the slot and feature specified.
+    */
+    IImsConfig getImsConfig(int slotId, int feature);
+
+    /**
      * Set the network selection mode to automatic.
      *
      * @param subId the id of the subscription to update.
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/net/Android.mk b/tests/net/Android.mk
index 994f3cc..9130e7d 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -39,6 +39,7 @@
     libc++ \
     libcrypto \
     libcutils \
+    libdexfile \
     libframeworksnettestsjni \
     libhidl-gen-utils \
     libhidlbase \
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index 2ac73db..56a075b 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -99,8 +99,12 @@
 
     @After
     public void tearDown() throws Exception {
-        if (mSocket != null) mSocket.close();
-        if (mServerSocket != null) mServerSocket.close();
+        mNMService.shutdown();
+        // Once NetworkManagementService#shutdown() actually does something and shutdowns
+        // the underlying NativeDaemonConnector, the block below should be uncommented.
+        // if (mOutputStream != null) mOutputStream.close();
+        // if (mSocket != null) mSocket.close();
+        // if (mServerSocket != null) mServerSocket.close();
     }
 
     /**
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 9f2cb92..8359fe2 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,7 +66,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -175,7 +174,6 @@
     }
 
     @Test
-    @Ignore
     public void testDefaultNetworkEvents() throws Exception {
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -294,7 +292,6 @@
     }
 
     @Test
-    @Ignore
     public void testEndToEndLogging() throws Exception {
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -635,6 +632,7 @@
         when(nai.getCurrentScore()).thenReturn(score);
         nai.linkProperties = new LinkProperties();
         nai.networkCapabilities = new NetworkCapabilities();
+        nai.lastValidated = true;
         for (int t : BitUtils.unpackBits(transports)) {
             nai.networkCapabilities.addTransportType(t);
         }
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/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();