Merge "Sealed sessions need to be destroyed or validated" into oc-mr1-dev
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index cec5db9..a558d68 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
-import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -52,8 +51,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * Accessibility services should only be used to assist users with disabilities in using
  * Android devices and apps. They run in the background and receive callbacks by the system
@@ -394,7 +391,7 @@
     public static final int SHOW_MODE_AUTO = 0;
     public static final int SHOW_MODE_HIDDEN = 1;
 
-    private int mConnectionId;
+    private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
     private AccessibilityServiceInfo mInfo;
 
@@ -1612,7 +1609,7 @@
 
         private final Callbacks mCallback;
 
-        private int mConnectionId;
+        private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
                 Callbacks callback) {
@@ -1707,7 +1704,8 @@
                     if (event != null) {
                         // Send the event to AccessibilityCache via AccessibilityInteractionClient
                         AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
-                        if (serviceWantsEvent) {
+                        if (serviceWantsEvent
+                                && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
                             // Send the event to AccessibilityService
                             mCallback.onAccessibilityEvent(event);
                         }
@@ -1721,7 +1719,9 @@
                 } return;
 
                 case DO_ON_INTERRUPT: {
-                    mCallback.onInterrupt();
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        mCallback.onInterrupt();
+                    }
                 } return;
 
                 case DO_INIT: {
@@ -1746,8 +1746,10 @@
                 } return;
 
                 case DO_ON_GESTURE: {
-                    final int gestureId = message.arg1;
-                    mCallback.onGesture(gestureId);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        final int gestureId = message.arg1;
+                        mCallback.onGesture(gestureId);
+                    }
                 } return;
 
                 case DO_CLEAR_ACCESSIBILITY_CACHE: {
@@ -1779,37 +1781,51 @@
                 } return;
 
                 case DO_ON_MAGNIFICATION_CHANGED: {
-                    final SomeArgs args = (SomeArgs) message.obj;
-                    final Region region = (Region) args.arg1;
-                    final float scale = (float) args.arg2;
-                    final float centerX = (float) args.arg3;
-                    final float centerY = (float) args.arg4;
-                    mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        final SomeArgs args = (SomeArgs) message.obj;
+                        final Region region = (Region) args.arg1;
+                        final float scale = (float) args.arg2;
+                        final float centerX = (float) args.arg3;
+                        final float centerY = (float) args.arg4;
+                        mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+                    }
                 } return;
 
                 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
-                    final int showMode = (int) message.arg1;
-                    mCallback.onSoftKeyboardShowModeChanged(showMode);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        final int showMode = (int) message.arg1;
+                        mCallback.onSoftKeyboardShowModeChanged(showMode);
+                    }
                 } return;
 
                 case DO_GESTURE_COMPLETE: {
-                    final boolean successfully = message.arg2 == 1;
-                    mCallback.onPerformGestureResult(message.arg1, successfully);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        final boolean successfully = message.arg2 == 1;
+                        mCallback.onPerformGestureResult(message.arg1, successfully);
+                    }
                 } return;
                 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
-                    mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
+                    }
                 } return;
                 case DO_ON_FINGERPRINT_GESTURE: {
-                    mCallback.onFingerprintGesture(message.arg1);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        mCallback.onFingerprintGesture(message.arg1);
+                    }
                 } return;
 
                 case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
-                    mCallback.onAccessibilityButtonClicked();
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        mCallback.onAccessibilityButtonClicked();
+                    }
                 } return;
 
                 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
-                    final boolean available = (message.arg1 != 0);
-                    mCallback.onAccessibilityButtonAvailabilityChanged(available);
+                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                        final boolean available = (message.arg1 != 0);
+                        mCallback.onAccessibilityButtonAvailabilityChanged(available);
+                    }
                 } return;
 
                 default :
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ad7a5ab..664bcbca 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -718,6 +718,10 @@
      * Cycles do not exist because they are illegal and screened for during installation.
      *
      * May be null if no splits are installed, or if no dependencies exist between them.
+     *
+     * NOTE: Any change to the way split dependencies are stored must update the logic that
+     *       creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts).
+     *
      * @hide
      */
     public SparseArray<int[]> splitDependencies;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 0d5c5e3..bfeb14d 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1769,7 +1769,7 @@
         }
 
         @Override
-        public void onRepeatingRequestError(long lastFrameNumber) {
+        public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
             if (DEBUG) {
                 Log.d(TAG, "Repeating request error received. Last frame number is " +
                         lastFrameNumber);
@@ -1782,7 +1782,10 @@
                 }
 
                 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
-                mRepeatingRequestId = REQUEST_ID_NONE;
+                // Check if there is already a new repeating request
+                if (mRepeatingRequestId == repeatingRequestId) {
+                    mRepeatingRequestId = REQUEST_ID_NONE;
+                }
             }
         }
 
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 135d92b..89ecd5f1c 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -77,7 +77,7 @@
         void onCaptureStarted(RequestHolder holder, long timestamp);
         void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
         void onRequestQueueEmpty();
-        void onRepeatingRequestError(long lastFrameNumber);
+        void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId);
     }
 
     /**
@@ -208,12 +208,14 @@
      * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
      *
      * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
+     * @param repeatingRequestId The ID of the repeating request being stopped
      */
-    public synchronized void setRepeatingRequestError(final long lastFrameNumber) {
+    public synchronized void setRepeatingRequestError(final long lastFrameNumber,
+            final int repeatingRequestId) {
         mCurrentHandler.post(new Runnable() {
             @Override
             public void run() {
-                mCurrentListener.onRepeatingRequestError(lastFrameNumber);
+                mCurrentListener.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
             }
         });
     }
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index d8df9a0..49d4096 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -264,10 +264,10 @@
         }
 
         @Override
-        public void onRepeatingRequestError(long lastFrameNumber) {
+        public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
+            Object[] objArray = new Object[] { lastFrameNumber, repeatingRequestId };
             Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
-                    /*arg1*/ (int) (lastFrameNumber & 0xFFFFFFFFL),
-                    /*arg2*/ (int) ( (lastFrameNumber >> 32) & 0xFFFFFFFFL));
+                    /*obj*/ objArray);
             getHandler().sendMessage(msg);
         }
 
@@ -329,9 +329,10 @@
                             break;
                         }
                         case REPEATING_REQUEST_ERROR: {
-                            long lastFrameNumber = msg.arg2 & 0xFFFFFFFFL;
-                            lastFrameNumber = (lastFrameNumber << 32) | (msg.arg1 & 0xFFFFFFFFL);
-                            mCallbacks.onRepeatingRequestError(lastFrameNumber);
+                            Object[] objArray = (Object[]) msg.obj;
+                            long lastFrameNumber = (Long) objArray[0];
+                            int repeatingRequestId = (Integer) objArray[1];
+                            mCallbacks.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
                             break;
                         }
                         case REQUEST_QUEUE_EMPTY: {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 621ea84..cb59fd1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -263,7 +263,8 @@
         }
 
         @Override
-        public void onRepeatingRequestError(final long lastFrameNumber) {
+        public void onRepeatingRequestError(final long lastFrameNumber,
+                final int repeatingRequestId) {
             mResultHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -271,7 +272,8 @@
                         Log.d(TAG, "doing onRepeatingRequestError callback.");
                     }
                     try {
-                        mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber);
+                        mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber,
+                                repeatingRequestId);
                     } catch (RemoteException e) {
                         throw new IllegalStateException(
                                 "Received remote exception during onRepeatingRequestError " +
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 565a43e..aaf07e6 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -939,7 +939,8 @@
                             Log.d(TAG, "Stopped repeating request. Last frame number is " +
                                     lastFrameNumber);
                         }
-                        mDeviceState.setRepeatingRequestError(lastFrameNumber);
+                        mDeviceState.setRepeatingRequestError(lastFrameNumber,
+                                burstHolder.getRequestId());
                     }
 
                     if (DEBUG) {
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 1dcaef4..3e08dcf 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -51,6 +51,7 @@
  *       Settings screen).
  * </ol>
  *
+ * <a name="BasicUsage"></a>
  * <h3>Basic usage</h3>
  *
  * <p>The basic autofill process is defined by the workflow below:
@@ -122,12 +123,14 @@
  * each {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} received - if it
  * doesn't, the request will eventually time out and be discarded by the Android System.
  *
+ * <a name="SavingUserData"></a>
  * <h3>Saving user data</h3>
  *
  * <p>If the service is also interested on saving the data filled by the user, it must set a
  * {@link SaveInfo} object in the {@link FillResponse}. See {@link SaveInfo} for more details and
  * examples.
  *
+ * <a name="UserAuthentication"></a>
  * <h3>User authentication</h3>
  *
  * <p>The service can provide an extra degree of security by requiring the user to authenticate
@@ -164,6 +167,7 @@
  * credentials in "vaults": the first response would contain fake datasets with the vault names,
  * and the subsequent response would contain the app credentials stored in that vault.
  *
+ * <a name="DataPartioning"></a>
  * <h3>Data partitioning</h3>
  *
  * <p>The autofillable views in a screen should be grouped in logical groups called "partitions".
@@ -243,6 +247,7 @@
  * <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
  * that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
  *
+ * <a name="PackageVerification"></a>
  * <h3>Package verification</h3>
  *
  * <p>When autofilling app-specific data (like username and password), the service must verify
@@ -270,9 +275,16 @@
  *   }
  *   return hash.toString();
  * }
- *
  * </pre>
  *
+ * <p>If the service did not store the signing certificates data the first time the data was saved
+ * &mdash; for example, because the data was created by a previous version of the app that did not
+ * use the Autofill Framework &mdash; the service should warn the user that the authenticity of the
+ * app cannot be confirmed (see an example on how to show such warning in the
+ * <a href="#WebSecurityDisclaimer">Web security</a> section below), and if the user agrees,
+ * then the service could save the data from the signing ceriticates for future use.
+ *
+ * <a name="IgnoringViews"></a>
  * <h3>Ignoring views</h3>
  *
  * <p>If the service find views that cannot be autofilled (for example, a text field representing
@@ -281,6 +293,7 @@
  * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
  * focused.
  *
+ * <a name="WebSecurity"></a>
  * <h3>Web security</h3>
  *
  * <p>When handling autofill requests that represent web pages (typically
@@ -313,6 +326,7 @@
  * }
  * </pre>
  *
+ * <a name="WebSecurityDisclaimer"></a>
  * <p>If the association between the web domain and app package cannot be verified through the steps
  * above, but the service thinks that it is appropriate to fill persisted credentials that are
  * stored for the web domain, the service should warn the user about the potential data
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index 8ab856e..2413e97 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -86,7 +86,7 @@
             }
             try {
                 final Matcher matcher = field.first.matcher(value);
-                if (!matcher.matches()) {
+                if (!matcher.find()) {
                     if (sDebug) Log.d(TAG, "match for " + field.first + " failed on id " + id);
                     return;
                 }
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index 682e7ee..d5e10d6 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -28,7 +28,6 @@
     void setOemUnlockAllowedByUser(boolean allowed);
     boolean isOemUnlockAllowedByUser();
 
-    boolean canUserAllowOemUnlock();
     boolean isOemUnlockAllowed();
     boolean isDeviceOemUnlocked();
 }
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index 3a56d9f..f0d6603 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -118,24 +118,6 @@
     }
 
     /**
-     * Returns whether all parties other than the user allow OEM unlock meaning the user can
-     * directly control whether or not the device can be OEM unlocked.
-     *
-     * If this is true, {@link #isOemUnlockAllowedByUser} is the same as {@link #isOemUnlockAllowed}
-     *
-     * @return Whether the user can directly control whether the device can be OEM unlocked.
-     *
-     * @hide
-     */
-    public boolean canUserAllowOemUnlock() {
-        try {
-            return mService.canUserAllowOemUnlock();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * @return Whether the bootloader is able to OEM unlock the device.
      *
      * @hide
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 969608b..4e06577 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -17,6 +17,9 @@
 package com.android.internal.policy;
 
 import android.graphics.Outline;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.Pair;
 import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
 import com.android.internal.R;
@@ -1103,8 +1106,8 @@
             boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
             int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
             updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
-                    mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge,
-                    navBarToLeftEdge,
+                    mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
+                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                     0 /* sideInset */, animate && !disallowAnimate, false /* force */);
 
             boolean statusBarNeedsRightInset = navBarToRightEdge
@@ -1114,7 +1117,7 @@
             int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                     : statusBarNeedsLeftInset ? mLastLeftInset : 0;
             updateColorViewInt(mStatusColorViewState, sysUiVisibility,
-                    calculateStatusBarColor(), mLastTopInset,
+                    calculateStatusBarColor(), 0, mLastTopInset,
                     false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
                     animate && !disallowAnimate,
                     mForceWindowDrawsStatusBarBackground);
@@ -1201,6 +1204,7 @@
      * @param state the color view to update.
      * @param sysUiVis the current systemUiVisibility to apply.
      * @param color the current color to apply.
+     * @param dividerColor the current divider color to apply.
      * @param size the current size in the non-parent-matching dimension.
      * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
      *                    horizontal edge,
@@ -1208,7 +1212,7 @@
      * @param animate if true, the change will be animated.
      */
     private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
-            int size, boolean verticalBar, boolean seascape, int sideMargin,
+            int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
             boolean animate, boolean force) {
         state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
         boolean show = state.attributes.isVisible(state.present, color,
@@ -1227,7 +1231,7 @@
         if (view == null) {
             if (showView) {
                 state.view = view = new View(mContext);
-                view.setBackgroundColor(color);
+                setColor(view, color, dividerColor, verticalBar, seascape);
                 view.setTransitionName(state.attributes.transitionName);
                 view.setId(state.attributes.id);
                 visibilityChanged = true;
@@ -1262,7 +1266,7 @@
                 view.setLayoutParams(lp);
             }
             if (showView) {
-                view.setBackgroundColor(color);
+                setColor(view, color, dividerColor, verticalBar, seascape);
             }
         }
         if (visibilityChanged) {
@@ -1295,6 +1299,34 @@
         state.color = color;
     }
 
+    private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
+            boolean seascape) {
+        if (dividerColor != 0) {
+            final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
+            if (dir == null || dir.first != verticalBar || dir.second != seascape) {
+                final int size = Math.round(
+                        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
+                                v.getContext().getResources().getDisplayMetrics()));
+                // Use an inset to make the divider line on the side that faces the app.
+                final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
+                        verticalBar && !seascape ? size : 0,
+                        !verticalBar ? size : 0,
+                        verticalBar && seascape ? size : 0, 0);
+                v.setBackground(new LayerDrawable(new Drawable[] {
+                        new ColorDrawable(dividerColor), d }));
+                v.setTag(new Pair<>(verticalBar, seascape));
+            } else {
+                final LayerDrawable d = (LayerDrawable) v.getBackground();
+                final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
+                ((ColorDrawable) inset.getDrawable()).setColor(color);
+                ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
+            }
+        } else {
+            v.setTag(null);
+            v.setBackgroundColor(color);
+        }
+    }
+
     private void updateColorViewTranslations() {
         // Put the color views back in place when they get moved off the screen
         // due to the the ViewRootImpl panning.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 57b0a73..5f1932c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -233,6 +233,7 @@
     private int mTextColor = 0;
     int mStatusBarColor = 0;
     int mNavigationBarColor = 0;
+    int mNavigationBarDividerColor = 0;
     private boolean mForcedStatusBarColor = false;
     private boolean mForcedNavigationBarColor = false;
 
@@ -2432,6 +2433,8 @@
         }
         if (!mForcedNavigationBarColor) {
             mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
+            mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
+                    0x00000000);
         }
 
         WindowManager.LayoutParams params = getAttributes();
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 12d707b..105ef44 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2056,6 +2056,14 @@
              Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
         <attr name="navigationBarColor" format="color" />
 
+        <!-- @hide
+             Shows 1dp line of the specified color between the navigation bar and the app content.
+             <p>For this to take effect, the window must be drawing the system bar backgrounds with
+             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
+               have been requested to be translucent with
+             {@link android.R.attr#windowTranslucentNavigation}. -->
+        <attr name="navigationBarDividerColor" format="color" />
+
         <!-- The duration, in milliseconds, of the window background fade duration
              when transitioning into or away from an Activity when called with an
              Activity Transition. Corresponds to
@@ -2088,6 +2096,16 @@
              {@link android.R.attr#windowBackground}.
              -->
         <attr name="windowSplashscreenContent" format="reference" />
+
+        <!-- @hide If set, the navigation bar will be drawn such that it is
+             compatible with a light navigation bar background.
+             <p>For this to take effect, the window must be drawing the system bar backgrounds with
+             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
+             have been requested to be translucent with
+             {@link android.R.attr#windowTranslucentNavigation}.
+             Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} on
+             the decor view. -->
+         <attr name="windowLightNavigationBar" format="boolean" />
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 8755e37..bf0c906 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -745,6 +745,11 @@
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorEdgeEffect">@android:color/black</item>
+
+        <!-- Add white nav bar with divider that matches material -->
+        <item name="navigationBarDividerColor">#1f000000</item>
+        <item name="navigationBarColor">@android:color/white</item>
+        <item name="windowLightNavigationBar">true</item>
     </style>
 
     <!-- @hide DeviceDefault theme for a window that should use Settings theme colors
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index c707240..a8d5164 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -275,6 +275,21 @@
     }
 
     @Test
+    public void testStepLoop() {
+        openTuner();
+
+        for (int i = 0; i < 10; i++) {
+            Log.d(TAG, "step loop iteration " + (i + 1));
+
+            int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
+            assertEquals(RadioManager.STATUS_OK, ret);
+            verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
+
+            resetCallback();
+        }
+    }
+
+    @Test
     public void testTuneAndGetPI() {
         openTuner();
 
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2d008c7..022198b 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -137,7 +137,7 @@
     mLooper->start(
             false,      // runOnCallingThread
             true,       // canCallJava
-            PRIORITY_FOREGROUND);
+            ANDROID_PRIORITY_VIDEO);
 
     if (nameIsType) {
         mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index fcb861c..cf5882f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -271,7 +271,7 @@
          * @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
          */
         @Override
-        public void onRepeatingRequestError(long lastFrameNumber) {
+        public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
             // TODO Auto-generated method stub
         }
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 476f016..e628b68 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -161,7 +161,7 @@
          * @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
          */
         @Override
-        public void onRepeatingRequestError(long lastFrameNumber) {
+        public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
             // TODO Auto-generated method stub
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index bf1bf34..40c2b1f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -339,26 +339,35 @@
         synchronized (mEntriesMap) {
             AppEntry entry = mEntriesMap.get(userId).get(packageName);
             if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                mBackgroundHandler.post(() -> {
-                    try {
-                        final StorageStats stats = mStats.queryStatsForPackage(
-                                entry.info.storageUuid, packageName, UserHandle.of(userId));
-                        final PackageStats legacy = new PackageStats(packageName, userId);
-                        legacy.codeSize = stats.getCodeBytes();
-                        legacy.dataSize = stats.getDataBytes();
-                        legacy.cacheSize = stats.getCacheBytes();
-                        try {
-                            mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true);
-                        } catch (RemoteException ignored) {
-                        }
-                    } catch (NameNotFoundException | IOException e) {
-                        Log.w(TAG, "Failed to query stats: " + e);
-                        try {
-                            mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false);
-                        } catch (RemoteException ignored) {
-                        }
-                    }
-                });
+                mBackgroundHandler.post(
+                        () -> {
+                            try {
+                                final StorageStats stats =
+                                        mStats.queryStatsForPackage(
+                                                entry.info.storageUuid,
+                                                packageName,
+                                                UserHandle.of(userId));
+                                final long cacheQuota =
+                                        mStats.getCacheQuotaBytes(
+                                                entry.info.storageUuid.toString(), entry.info.uid);
+                                final PackageStats legacy = new PackageStats(packageName, userId);
+                                legacy.codeSize = stats.getCodeBytes();
+                                legacy.dataSize = stats.getDataBytes();
+                                legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota);
+                                try {
+                                    mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
+                                            legacy, true);
+                                } catch (RemoteException ignored) {
+                                }
+                            } catch (NameNotFoundException | IOException e) {
+                                Log.w(TAG, "Failed to query stats: " + e);
+                                try {
+                                    mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
+                                            null, false);
+                                } catch (RemoteException ignored) {
+                                }
+                            }
+                        });
             }
             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
         }
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index b27dedd..2148c80 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -59,6 +59,7 @@
     <item type="id" name="transformation_start_y_tag"/>
     <item type="id" name="transformation_start_scale_x_tag"/>
     <item type="id" name="transformation_start_scale_y_tag"/>
+    <item type="id" name="continuous_clipping_tag"/>
 
     <!-- Whether the icon is from a notification for which targetSdk < L -->
     <item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 017365f..176112b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -31,6 +31,7 @@
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
 import android.service.quicksettings.TileService;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
@@ -51,6 +52,8 @@
 public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
     public static final String PREFIX = "custom(";
 
+    private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS;
+
     private static final boolean DEBUG = false;
 
     // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
@@ -83,6 +86,11 @@
         mUser = ActivityManager.getCurrentUser();
     }
 
+    @Override
+    protected long getStaleTimeout() {
+        return CUSTOM_STALE_TIMEOUT + DateUtils.MINUTE_IN_MILLIS * mHost.indexOf(getTileSpec());
+    }
+
     private void setTileIcon() {
         try {
             PackageManager pm = mContext.getPackageManager();
@@ -186,7 +194,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c04daf..6a5530f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -32,10 +32,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.service.quicksettings.Tile;
+import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.Utils;
@@ -45,7 +47,6 @@
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.PagedTileLayout;
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
 
@@ -62,14 +63,18 @@
     protected final String TAG = "Tile." + getClass().getSimpleName();
     protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
 
+    private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
+
     protected final QSHost mHost;
     protected final Context mContext;
-    protected final H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
+    // @NonFinalForTesting
+    protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
     private final ArraySet<Object> mListeners = new ArraySet<>();
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final Object mStaleListener = new Object();
     protected TState mState = newTileState();
     private TState mTmpState = newTileState();
     private boolean mAnnounceNextStateChange;
@@ -95,6 +100,7 @@
     protected QSTileImpl(QSHost host) {
         mHost = host;
         mContext = host.getContext();
+        handleStale(); // Tile was just created, must be stale.
     }
 
     /**
@@ -106,6 +112,7 @@
             if (mListeners.add(listener) && mListeners.size() == 1) {
                 if (DEBUG) Log.d(TAG, "setListening " + true);
                 mHandler.obtainMessage(H.SET_LISTENING, 1, 0).sendToTarget();
+                refreshState(); // Ensure we get at least one refresh after listening.
             }
         } else {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
@@ -115,6 +122,15 @@
         }
     }
 
+    protected long getStaleTimeout() {
+        return DEFAULT_STALE_TIMEOUT;
+    }
+
+    @VisibleForTesting
+    protected void handleStale() {
+        setListening(mStaleListener, true);
+    }
+
     public String getTileSpec() {
         return mTileSpec;
     }
@@ -273,6 +289,9 @@
         if (changed) {
             handleStateChanged();
         }
+        mHandler.removeMessages(H.STALE);
+        mHandler.sendEmptyMessageDelayed(H.STALE, getStaleTimeout());
+        setListening(mStaleListener, false);
     }
 
     private void handleStateChanged() {
@@ -326,11 +345,11 @@
         handleRefreshState(null);
     }
 
-    protected abstract void setListening(boolean listening);
+    protected abstract void handleSetListening(boolean listening);
 
     protected void handleDestroy() {
         if (mListeners.size() != 0) {
-            setListening(false);
+            handleSetListening(false);
         }
         mCallbacks.clear();
     }
@@ -380,8 +399,10 @@
         private static final int REMOVE_CALLBACKS = 12;
         private static final int REMOVE_CALLBACK = 13;
         private static final int SET_LISTENING = 14;
+        private static final int STALE = 15;
 
-        private H(Looper looper) {
+        @VisibleForTesting
+        protected H(Looper looper) {
             super(looper);
         }
 
@@ -436,8 +457,11 @@
                     name = "handleClearState";
                     handleClearState();
                 } else if (msg.what == SET_LISTENING) {
-                    name = "setListening";
-                    setListening(msg.arg1 != 0);
+                    name = "handleSetListening";
+                    handleSetListening(msg.arg1 != 0);
+                } else if (msg.what == STALE) {
+                    name = "handleStale";
+                    handleStale();
                 } else {
                     throw new IllegalArgumentException("Unknown msg: " + msg.what);
                 }
@@ -515,7 +539,7 @@
         }
     }
 
-    protected class AnimationIcon extends ResourceIcon {
+    protected static class AnimationIcon extends ResourceIcon {
         private final int mAnimatedResId;
 
         public AnimationIcon(int resId, int staticResId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2e7012e..bef1aff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -110,7 +110,7 @@
         }
     }
 
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 3f419a8..95504ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -55,7 +55,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mBatteryController.addCallback(this);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index bc6233d..774f0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -76,7 +76,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mController.addCallback(mCallback);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 2fc9fc7..fb396b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -91,9 +91,9 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mController == null) return;
-        if (DEBUG) Log.d(TAG, "setListening " + listening);
+        if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
         if (listening) {
             mController.addCallback(mCallback);
             mKeyguard.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index f900da0..0bb7479 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -92,7 +92,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mController.addCallback(mSignalCallback);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 40fe484..b93f1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         mSetting.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 8b62beb..a102696 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -45,7 +45,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mDataSaverController.addCallback(this);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 5938749..9e265e22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -232,7 +232,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
         if (mListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index e6ac908..f2ead1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -54,7 +54,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mFlashlightController.addCallback(this);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index c17573d..910b6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 00cfbfa..4f4004c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -76,7 +76,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 5e66334..c35f591 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -55,7 +55,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mController.addCallback(mCallback);
             mKeyguard.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 6500740..b3ff4e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -51,7 +51,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         mListening = listening;
         if (mListening) {
             mContext.registerReceiver(mNfcReceiver,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2a12769..4c20361 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -95,7 +95,7 @@
     }
 
     @Override
-    protected void setListening(boolean listening) {
+    protected void handleSetListening(boolean listening) {
         mIsListening = listening;
         if (listening) {
             mController.setListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index fb937bd..1e00894 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -62,7 +62,7 @@
         return new BooleanState();
     }
 
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (mController == null) return;
         if (listening) {
             mController.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index d6043f4..bde1c98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -69,7 +69,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mUserInfoController.addCallback(this);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 136cf21..33b1512 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -75,7 +75,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mController.addCallback(mSignalCallback);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 38821f6..5f7d6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -48,7 +48,7 @@
     }
 
     @Override
-    public void setListening(boolean listening) {
+    public void handleSetListening(boolean listening) {
         if (listening) {
             mProfileController.addCallback(this);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f844866..f545556 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -65,6 +65,7 @@
 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
 import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -119,6 +120,7 @@
     private boolean mFinishedOnStartup;
     private boolean mIgnoreAltTabRelease;
     private boolean mIsVisible;
+    private boolean mRecentsStartRequested;
     private Configuration mLastConfig;
 
     // Top level views
@@ -416,6 +418,7 @@
             launchState.launchedFromHome = false;
             onEnterAnimationComplete();
         }
+        mRecentsStartRequested = false;
     }
 
     @Override
@@ -449,7 +452,6 @@
      * Reloads the stack views upon launching Recents.
      */
     private void reloadStackView() {
-
         // If the Recents component has preloaded a load plan, then use that to prevent
         // reconstructing the task stack
         RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -572,7 +574,9 @@
         MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
         Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(false);
 
-        if (!isChangingConfigurations()) {
+        // When recents starts again before onStop, do not reset launch flags so entrance animation
+        // can run
+        if (!isChangingConfigurations() && !mRecentsStartRequested) {
             // Workaround for b/22542869, if the RecentsActivity is started again, but without going
             // through SystemUI, we need to reset the config launch flags to ensure that we do not
             // wait on the system to send a signal that was never queued.
@@ -718,6 +722,10 @@
         MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
     }
 
+    public final void onBusEvent(RecentsActivityStartingEvent event) {
+        mRecentsStartRequested = true;
+    }
+
     public final void onBusEvent(UserInteractionEvent event) {
         // Stop the fast-toggle dozer
         mIterateTrigger.stopDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 5eefe9a..184b95d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -22,10 +22,12 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.systemui.Interpolators;
@@ -53,6 +55,7 @@
             SystemProperties.getBoolean("debug.icon_opening_animations", true);
     private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
+    private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private ViewInvertHelper mViewInvertHelper;
     private boolean mDark;
     private NotificationIconContainer mShelfIcons;
@@ -305,6 +308,16 @@
         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
         mShelfIcons.calculateIconTranslations();
         mShelfIcons.applyIconStates();
+        for (int i = 0; i < mHostLayout.getChildCount(); i++) {
+            View child = mHostLayout.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)
+                    || child.getVisibility() == GONE) {
+                continue;
+            }
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            updateIconClipAmount(row);
+            updateContinuousClipping(row);
+        }
         boolean hideBackground = numViewsInShelf < 1.0f;
         setHideBackground(hideBackground || backgroundForceHidden);
         if (mNotGoneIndex == -1) {
@@ -312,6 +325,43 @@
         }
     }
 
+    private void updateIconClipAmount(ExpandableNotificationRow row) {
+        float maxTop = row.getTranslationY();
+        StatusBarIconView icon = row.getEntry().expandedIcon;
+        float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
+        if (shelfIconPosition < maxTop) {
+            int top = (int) (maxTop - shelfIconPosition);
+            Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
+            icon.setClipBounds(clipRect);
+        } else {
+            icon.setClipBounds(null);
+        }
+    }
+
+    private void updateContinuousClipping(final ExpandableNotificationRow row) {
+        StatusBarIconView icon = row.getEntry().expandedIcon;
+        boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+        boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
+        if (needsContinuousClipping && !isContinuousClipping) {
+            ViewTreeObserver.OnPreDrawListener predrawListener =
+                    new ViewTreeObserver.OnPreDrawListener() {
+                        @Override
+                        public boolean onPreDraw() {
+                            boolean animatingY = ViewState.isAnimatingY(icon);
+                            if (!animatingY || !icon.isAttachedToWindow()) {
+                                icon.getViewTreeObserver().removeOnPreDrawListener(this);
+                                icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
+                                return true;
+                            }
+                            updateIconClipAmount(row);
+                            return true;
+                        }
+                    };
+            icon.getViewTreeObserver().addOnPreDrawListener(predrawListener);
+            icon.setTag(TAG_CONTINUOUS_CLIPPING, predrawListener);
+        }
+    }
+
     private void updateNotificationClipHeight(ExpandableNotificationRow row,
             float notificationClipEnd) {
         float viewEnd = row.getTranslationY() + row.getActualHeight();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index d823981..004ff29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -25,10 +25,15 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.lang.Thread.sleep;
+
 import android.content.Intent;
 import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
@@ -67,10 +72,10 @@
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mHost = mock(QSTileHost.class);
         when(mHost.indexOf(spec)).thenReturn(POSITION);
-        mTestableLooper.runWithLooper(() -> {
-            mTile = new TileImpl(mHost);
-            mTile.setTileSpec(spec);
-        });
+
+        mTile = spy(new TileImpl(mHost));
+        mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
+        mTile.setTileSpec(spec);
     }
 
     @Test
@@ -102,6 +107,31 @@
         verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
     }
 
+    @Test
+    public void testStaleTimeout() throws InterruptedException {
+        when(mTile.getStaleTimeout()).thenReturn(5l);
+        clearInvocations(mTile);
+
+        mTile.handleRefreshState(null);
+        mTestableLooper.processAllMessages();
+        verify(mTile, never()).handleStale();
+
+        sleep(10);
+        mTestableLooper.processAllMessages();
+        verify(mTile).handleStale();
+    }
+
+    @Test
+    public void testStaleListening() {
+        mTile.handleStale();
+        mTestableLooper.processAllMessages();
+        verify(mTile).handleSetListening(eq(true));
+
+        mTile.handleRefreshState(null);
+        mTestableLooper.processAllMessages();
+        verify(mTile).handleSetListening(eq(false));
+    }
+
     private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
 
         private final int mCategory;
@@ -166,7 +196,7 @@
         }
 
         @Override
-        protected void setListening(boolean listening) {
+        protected void handleSetListening(boolean listening) {
 
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
index 12a4399..f8fd53d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
@@ -185,7 +185,9 @@
         countDownLatch.await();
     }
 
+    /* Cancelling requires us to be on the UI thread otherwise we might have a race */
     @Test
+    @UiThreadTest
     public void testSupersedesExistingTask() throws Exception {
         mNotificationInflater.inflateNotificationViews();
         mNotificationInflater.setIsLowPriority(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index a915cb20..b22a646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -27,7 +27,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
@@ -145,7 +144,6 @@
     @Test
     @RunWithLooper
     public void testSortOrder() {
-        Log.d("TestTest", "Config " + mContext.getResources().getConfiguration().uiMode);
         Object def = new Object();
         Object uiMode = new Object();
         Object tuner = new Object();
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index eb895de..8156025 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4305,6 +4305,12 @@
     // OS: O MR
     ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES = 1096;
 
+    // FIELD - Whether smart suggestion ranking was enabled or not
+    // Type: int encoded boolean
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED = 1097;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
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 24f3b33..371e74d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -229,6 +229,13 @@
     public void setFilterText(@Nullable String filterText) {
         throwIfDestroyed();
         if (mAdapter == null) {
+            // ViewState doesn't not support filtering - typically when it's for an authenticated
+            // FillResponse.
+            if (TextUtils.isEmpty(filterText)) {
+                mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+            } else {
+                mCallback.requestHideFillUi();
+            }
             return;
         }
 
@@ -510,8 +517,9 @@
                         final ViewItem item = mAllItems.get(i);
                         final String value = item.getValue();
                         // No value, i.e. null, matches any filter
-                        if (value == null
-                                || value.toLowerCase().startsWith(constraintLowerCase)) {
+                        if ((value == null && item.mDataset.getAuthentication() == null)
+                                || (value != null
+                                        && value.toLowerCase().startsWith(constraintLowerCase))) {
                             filteredItems.add(item);
                         }
                     }
@@ -525,9 +533,11 @@
                     final boolean resultCountChanged;
                     final int oldItemCount = mFilteredItems.size();
                     mFilteredItems.clear();
-                    @SuppressWarnings("unchecked")
-                    final List<ViewItem> items = (List<ViewItem>) results.values;
-                    mFilteredItems.addAll(items);
+                    if (results.count > 0) {
+                        @SuppressWarnings("unchecked")
+                        final List<ViewItem> items = (List<ViewItem>) results.values;
+                        mFilteredItems.addAll(items);
+                    }
                     resultCountChanged = (oldItemCount != mFilteredItems.size());
                     if (resultCountChanged) {
                         announceSearchResultIfNeeded();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 791b2c0..56ae57b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1214,6 +1214,11 @@
             // Still waiting for something to pause; can't sleep yet.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
             shouldSleep = false;
+        } else if (mLastPausedActivity == topActivity()) {
+            // Our top activity is currently paused, we need to ensure we move it to the stopped
+            // state.
+            stopActivityLocked(mLastPausedActivity);
+            shouldSleep = false;
         }
 
         if (!shuttingDown) {
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 5e19b13..40c6639 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -149,8 +149,12 @@
 
             final long token = Binder.clearCallingIdentity();
             try {
-                if (!canUserAllowOemUnlock()) {
-                    throw new SecurityException("User cannot allow OEM unlock");
+                if (!isOemUnlockAllowedByAdmin()) {
+                    throw new SecurityException("Admin does not allow OEM unlock");
+                }
+
+                if (!mOemLock.isOemUnlockAllowedByCarrier()) {
+                    throw new SecurityException("Carrier does not allow OEM unlock");
                 }
 
                 mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
@@ -172,18 +176,6 @@
         }
 
         @Override
-        public boolean canUserAllowOemUnlock() {
-            enforceOemUnlockReadPermission();
-
-            final long token = Binder.clearCallingIdentity();
-            try {
-                return isOemUnlockAllowedByAdmin() && mOemLock.isOemUnlockAllowedByCarrier();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
         public boolean isOemUnlockAllowed() {
             enforceOemUnlockReadPermission();
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 698d387..0f580d8 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -147,8 +147,13 @@
         // Get the class loader context dependencies.
         // For each code path in the package, this array contains the class loader context that
         // needs to be passed to dexopt in order to ensure correct optimizations.
+        boolean[] pathsWithCode = new boolean[paths.size()];
+        pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+        for (int i = 1; i < paths.size(); i++) {
+            pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+        }
         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
-                pkg.applicationInfo, sharedLibraries);
+                pkg.applicationInfo, sharedLibraries, pathsWithCode);
 
         // Sanity check that we do not call dexopt with inconsistent data.
         if (paths.size() != classLoaderContexts.length) {
@@ -164,10 +169,15 @@
         int result = DEX_OPT_SKIPPED;
         for (int i = 0; i < paths.size(); i++) {
             // Skip paths that have no code.
-            if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
-                    (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
+            if (!pathsWithCode[i]) {
                 continue;
             }
+            if (classLoaderContexts[i] == null) {
+                throw new IllegalStateException("Inconsistent information in the "
+                        + "package structure. A split is marked to contain code "
+                        + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
+            }
+
             // Append shared libraries with split dependencies for this split.
             String path = paths.get(i);
             if (options.getSplitName() != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 0196212..ef2ee4a 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -35,7 +35,9 @@
     /**
      * Creates the class loader context dependencies for each of the application code paths.
      * The returned array contains the class loader contexts that needs to be passed to dexopt in
-     * order to ensure correct optimizations.
+     * order to ensure correct optimizations. "Code" paths with no actual code, as specified by
+     * {@param pathsWithCode}, are ignored and will have null as their context in the returned array
+     * (configuration splits are an example of paths without code).
      *
      * A class loader context describes how the class loader chain should be built by dex2oat
      * in order to ensure that classes are resolved during compilation as they would be resolved
@@ -60,7 +62,8 @@
      * {@link android.app.LoadedApk#makePaths(
      * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
      */
-    public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) {
+    public static String[] getClassLoaderContexts(ApplicationInfo info,
+            String[] sharedLibraries, boolean[] pathsWithCode) {
         // The base class loader context contains only the shared library.
         String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
         String baseApkContextClassLoader = encodeClassLoader(
@@ -86,7 +89,7 @@
         // Index 0 is the class loaded context for the base apk.
         // Index `i` is the class loader context encoding for split `i`.
         String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
-        classLoaderContexts[0] = baseApkContextClassLoader;
+        classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
 
         if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) {
             // If the app didn't request for the splits to be loaded in isolation or if it does not
@@ -94,7 +97,15 @@
             // apk class loader (in the order of their definition).
             String classpath = sharedLibrariesAndBaseClassPath;
             for (int i = 1; i < classLoaderContexts.length; i++) {
-                classLoaderContexts[i] = encodeClassLoader(classpath, info.classLoaderName);
+                classLoaderContexts[i] = pathsWithCode[i]
+                        ? encodeClassLoader(classpath, info.classLoaderName) : null;
+                // Note that the splits with no code are not removed from the classpath computation.
+                // i.e. split_n might get the split_n-1 in its classpath dependency even
+                // if split_n-1 has no code.
+                // The splits with no code do not matter for the runtime which ignores
+                // apks without code when doing the classpath checks. As such we could actually
+                // filter them but we don't do it in order to keep consistency with how the apps
+                // are loaded.
                 classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]);
             }
         } else {
@@ -116,9 +127,17 @@
             String splitDependencyOnBase = encodeClassLoader(
                     sharedLibrariesAndBaseClassPath, info.classLoaderName);
             SparseArray<int[]> splitDependencies = info.splitDependencies;
+
+            // Note that not all splits have dependencies (e.g. configuration splits)
+            // The splits without dependencies will have classLoaderContexts[config_split_index]
+            // set to null after this step.
             for (int i = 1; i < splitDependencies.size(); i++) {
-                getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache,
-                        splitDependencies, classLoaderContexts, splitDependencyOnBase);
+                int splitIndex = splitDependencies.keyAt(i);
+                if (pathsWithCode[splitIndex]) {
+                    // Compute the class loader context only for the splits with code.
+                    getParentDependencies(splitIndex, splitClassLoaderEncodingCache,
+                            splitDependencies, classLoaderContexts, splitDependencyOnBase);
+                }
             }
 
             // At this point classLoaderContexts contains only the parent dependencies.
@@ -126,8 +145,17 @@
             // come first in the context.
             for (int i = 1; i < classLoaderContexts.length; i++) {
                 String splitClassLoader = encodeClassLoader("", info.splitClassLoaderNames[i - 1]);
-                classLoaderContexts[i] = encodeClassLoaderChain(
-                        splitClassLoader, classLoaderContexts[i]);
+                if (pathsWithCode[i]) {
+                    // If classLoaderContexts[i] is null it means that the split does not have
+                    // any dependency. In this case its context equals its declared class loader.
+                    classLoaderContexts[i] = classLoaderContexts[i] == null
+                            ? splitClassLoader
+                            : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+                } else {
+                    // This is a split without code, it has no dependency and it is not compiled.
+                    // Its context will be null.
+                    classLoaderContexts[i] = null;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dd887e0..64ac7ac 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1657,7 +1657,14 @@
                     if (mWin.mAppToken != null) {
                         mWin.mAppToken.setCanTurnScreenOn(false);
                     }
-                    mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+
+                    // We do not add {@code SET_TURN_ON_SCREEN} when the screen is already
+                    // interactive as the value may persist until the next animation, which could
+                    // potentially occurring while turning off the screen. This would lead to the
+                    // screen incorrectly turning back on.
+                    if (!mService.mPowerManager.isInteractive()) {
+                        mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+                    }
                 }
             }
             if (hasSurface()) {
diff --git a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
index 85ec9e0..81d46f3 100644
--- a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
+++ b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
@@ -48,15 +48,19 @@
         return;
     }
 
-    while (!mExiting) {
-        ALOGV("Waiting for task...");
+    while (true) {
         Task task;
         {
             unique_lock<mutex> lk(mQueueMutex);
-            mQueueCond.wait(lk);
-            if (mExiting) break;
 
-            if (mQueue.empty()) continue;
+            if (mExiting) break;
+            if (mQueue.empty()) {
+                ALOGV("Waiting for task...");
+                mQueueCond.wait(lk);
+                if (mExiting) break;
+                if (mQueue.empty()) continue;
+            }
+
             task = mQueue.front();
             mQueue.pop();
         }
@@ -74,6 +78,7 @@
     ALOGE_IF(res != JNI_OK, "Couldn't detach thread");
 
     ALOGV("Native callback thread %p finished", this);
+    ALOGD_IF(!mQueue.empty(), "Skipped execution of %zu tasks", mQueue.size());
 }
 
 void NativeCallbackThread::enqueue(const Task &task) {
@@ -84,6 +89,7 @@
         return;
     }
 
+    ALOGV("Adding task to the queue...");
     mQueue.push(task);
     mQueueCond.notify_one();
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 2c56a82..c8c8eb1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -46,22 +46,35 @@
     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
             DelegateLastClassLoader.class.getName();
 
-    private ApplicationInfo createMockApplicationInfo(String baseClassLoader, boolean addSplits,
+    private static class TestData {
+        ApplicationInfo info;
+        boolean[] pathsWithCode;
+    }
+
+    private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
             boolean addSplitDependencies) {
         ApplicationInfo ai = new ApplicationInfo();
         String codeDir = "/data/app/mock.android.com";
         ai.setBaseCodePath(codeDir + "/base.dex");
         ai.classLoaderName = baseClassLoader;
         ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        boolean[] pathsWithCode;
+        if (!addSplits) {
+            pathsWithCode = new boolean[] {true};
+        } else {
+            pathsWithCode = new boolean[9];
+            Arrays.fill(pathsWithCode, true);
+            pathsWithCode[7] = false;  // config split
 
-        if (addSplits) {
             ai.setSplitCodePaths(new String[]{
                     codeDir + "/base-1.dex",
                     codeDir + "/base-2.dex",
                     codeDir + "/base-3.dex",
                     codeDir + "/base-4.dex",
                     codeDir + "/base-5.dex",
-                    codeDir + "/base-6.dex"});
+                    codeDir + "/base-6.dex",
+                    codeDir + "/config-split-7.dex",
+                    codeDir + "/feature-no-deps.dex"});
 
             ai.splitClassLoaderNames = new String[]{
                     DELEGATE_LAST_CLASS_LOADER_NAME,
@@ -69,7 +82,9 @@
                     PATH_CLASS_LOADER_NAME,
                     DEX_CLASS_LOADER_NAME,
                     PATH_CLASS_LOADER_NAME,
-                    null};  // A null class loader name should default to PathClassLoader.
+                    null,   // A null class loader name should default to PathClassLoader.
+                    null,   // The config split gets a null class loader.
+                    null};  // The feature split with no dependency and no specified class loader.
             if (addSplitDependencies) {
                 ai.splitDependencies = new SparseArray<>(ai.splitClassLoaderNames.length + 1);
                 ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
@@ -79,18 +94,24 @@
                 ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
                 ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
                 ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
+                // Do not add the config split to the dependency list.
+                // Do not add the feature split with no dependency to the dependency list.
             }
         }
-        return ai;
+        TestData data = new TestData();
+        data.info = ai;
+        data.pathsWithCode = pathsWithCode;
+        return data;
     }
 
     @Test
     public void testSplitChain() {
-        ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
-        assertEquals(7, contexts.length);
+        assertEquals(9, contexts.length);
         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
                 contexts[1]);
@@ -99,15 +120,18 @@
         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
         assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals(null, contexts[7]);  // config split
+        assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
     }
 
     @Test
     public void testSplitChainNoSplitDependencies() {
-        ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
-        assertEquals(7, contexts.length);
+        assertEquals(9, contexts.length);
         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
         assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
         assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
@@ -119,15 +143,21 @@
         assertEquals(
                 "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
                 contexts[6]);
+        assertEquals(null, contexts[7]);  // config split
+        assertEquals(
+                "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+                contexts[8]);  // feature split with no dependency
     }
 
     @Test
     public void testSplitChainNoIsolationNoSharedLibrary() {
-        ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
-        ai.privateFlags = ai.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+        data.info.privateFlags = data.info.privateFlags
+                & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, null, data.pathsWithCode);
 
-        assertEquals(7, contexts.length);
+        assertEquals(9, contexts.length);
         assertEquals("PCL[]", contexts[0]);
         assertEquals("PCL[base.dex]", contexts[1]);
         assertEquals("PCL[base.dex:base-1.dex]", contexts[2]);
@@ -137,14 +167,20 @@
         assertEquals(
                 "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
                 contexts[6]);
+        assertEquals(null, contexts[7]);  // config split
+        assertEquals(
+                "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+                contexts[8]);  // feature split with no dependency
     }
+
     @Test
     public void testSplitChainNoSharedLibraries() {
-        ApplicationInfo ai = createMockApplicationInfo(
+        TestData data = createMockApplicationInfo(
                 DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, null, data.pathsWithCode);
 
-        assertEquals(7, contexts.length);
+        assertEquals(9, contexts.length);
         assertEquals("DLC[]", contexts[0]);
         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];DLC[base.dex]", contexts[1]);
         assertEquals("DLC[];PCL[base-4.dex];DLC[base.dex]", contexts[2]);
@@ -152,16 +188,19 @@
         assertEquals("PCL[];DLC[base.dex]", contexts[4]);
         assertEquals("PCL[];DLC[base.dex]", contexts[5]);
         assertEquals("PCL[];PCL[base-5.dex];DLC[base.dex]", contexts[6]);
+        assertEquals(null, contexts[7]);  // config split
+        assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
     }
 
     @Test
     public void testSplitChainWithNullPrimaryClassLoader() {
         // A null classLoaderName should mean PathClassLoader.
-        ApplicationInfo ai = createMockApplicationInfo(null, true, true);
+        TestData data = createMockApplicationInfo(null, true, true);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
-        assertEquals(7, contexts.length);
+        assertEquals(9, contexts.length);
         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
         assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
@@ -169,13 +208,16 @@
         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
         assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals(null, contexts[7]);  // config split
+        assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
     }
 
     @Test
     public void tesNoSplits() {
-        ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -183,9 +225,10 @@
 
     @Test
     public void tesNoSplitsNullClassLoaderName() {
-        ApplicationInfo ai = createMockApplicationInfo(null, false, false);
+        TestData data = createMockApplicationInfo(null, false, false);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -193,10 +236,11 @@
 
     @Test
     public void tesNoSplitDelegateLast() {
-        ApplicationInfo ai = createMockApplicationInfo(
+        TestData data = createMockApplicationInfo(
                 DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("DLC[a.dex:b.dex]", contexts[0]);
@@ -204,8 +248,9 @@
 
     @Test
     public void tesNoSplitsNoSharedLibraries() {
-        ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, null, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[]", contexts[0]);
@@ -213,15 +258,55 @@
 
     @Test
     public void tesNoSplitDelegateLastNoSharedLibraries() {
-        ApplicationInfo ai = createMockApplicationInfo(
+        TestData data = createMockApplicationInfo(
                 DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
-        String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, null, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("DLC[]", contexts[0]);
     }
 
     @Test
+    public void testContextWithNoCode() {
+        TestData data = createMockApplicationInfo(null, true, false);
+        Arrays.fill(data.pathsWithCode, false);
+
+        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
+
+        assertEquals(9, contexts.length);
+        assertEquals(null, contexts[0]);
+        assertEquals(null, contexts[1]);
+        assertEquals(null, contexts[2]);
+        assertEquals(null, contexts[3]);
+        assertEquals(null, contexts[4]);
+        assertEquals(null, contexts[5]);
+        assertEquals(null, contexts[6]);
+        assertEquals(null, contexts[7]);
+    }
+
+    @Test
+    public void testContextBaseNoCode() {
+        TestData data = createMockApplicationInfo(null, true, true);
+        data.pathsWithCode[0] = false;
+        String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+        String[] contexts = DexoptUtils.getClassLoaderContexts(
+                data.info, sharedLibrary, data.pathsWithCode);
+
+        assertEquals(9, contexts.length);
+        assertEquals(null, contexts[0]);
+        assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
+        assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+        assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+        assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+        assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+        assertEquals(null, contexts[7]);
+    }
+
+    @Test
     public void testProcessContextForDexLoad() {
         List<String> classLoaders = Arrays.asList(
                 DELEGATE_LAST_CLASS_LOADER_NAME,
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index 4c3f7e7..1e8cf18 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -18,19 +18,19 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.IDownloadProgressListener;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsDownloadReceiver;
 import android.telephony.mbms.MbmsException;
@@ -44,6 +44,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -52,147 +53,38 @@
 public class MbmsDownloadManager {
     private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
 
+    /** @hide */
+    // TODO: systemapi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
             "android.telephony.action.EmbmsDownload";
-    /**
-     * The MBMS middleware should send this when a download of single file has completed or
-     * failed. Mandatory extras are
-     * {@link #EXTRA_RESULT}
-     * {@link #EXTRA_FILE_INFO}
-     * {@link #EXTRA_REQUEST}
-     * {@link #EXTRA_TEMP_LIST}
-     * {@link #EXTRA_FINAL_URI}
-     *
-     * TODO: future systemapi
-     */
-    public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
-            "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
-
-    /**
-     * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
-     * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
-     * extras are
-     * {@link #EXTRA_REQUEST}
-     *
-     * Optional extras are
-     * {@link #EXTRA_FD_COUNT} (0 if not present)
-     * {@link #EXTRA_PAUSED_LIST} (empty if not present)
-     *
-     * TODO: future systemapi
-     */
-    public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
-            "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
-
-    /**
-     * The MBMS middleware should send this when it wishes to clean up temp  files in the app's
-     * filesystem. Mandatory extras are:
-     * {@link #EXTRA_TEMP_FILES_IN_USE}
-     *
-     * TODO: future systemapi
-     */
-    public static final String ACTION_CLEANUP =
-            "android.telephony.mbms.action.CLEANUP";
 
     /**
      * Integer extra indicating the result code of the download. One of
      * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
-     * TODO: Not systemapi.
      */
     public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
 
     /**
      * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
      * is for. Must not be null.
-     * TODO: Not systemapi.
      */
     public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
 
     /**
-     * Extra containing the {@link DownloadRequest} for which the download result or file
-     * descriptor request is for. Must not be null.
-     * TODO: future systemapi (here and and all extras) except the three for the app intent
-     */
-    public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
-
-    /**
-     * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
-     * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
-     * files will be deleted upon receipt of the intent.
-     * May be null.
-     */
-    public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
-
-    /**
-     * Extra containing a single {@link Uri} indicating the path to the temp file in which the
-     * decoded downloaded file resides. Must not be null.
-     */
-    public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
-
-    /**
-     * Extra containing an integer indicating the number of temp files requested.
-     */
-    public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
-
-    /**
-     * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
-     * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
-     * should have scheme {@code file://}.
-     */
-    public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
-
-    /**
-     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
-     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
-     * to be used for new file downloads.
-     */
-    public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
-
-    /**
-     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
-     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
-     * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
-     * access to previously paused downloads.
-     */
-    public static final String EXTRA_PAUSED_URI_LIST =
-            "android.telephony.mbms.extra.PAUSED_URI_LIST";
-
-    /**
-     * Extra containing a string that points to the middleware's knowledge of where the temp file
-     * root for the app is. The path should be a canonical path as returned by
-     * {@link File#getCanonicalPath()}
-     */
-    public static final String EXTRA_TEMP_FILE_ROOT =
-            "android.telephony.mbms.extra.TEMP_FILE_ROOT";
-
-    /**
-     * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
-     * still using.
-     */
-    public static final String EXTRA_TEMP_FILES_IN_USE =
-            "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
-
-    /**
-     * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
-     * file-descriptor requests and cleanup requests to specify which service they want to
-     * request temp files or clean up temp files for, respectively.
-     */
-    public static final String EXTRA_SERVICE_INFO =
-            "android.telephony.mbms.extra.SERVICE_INFO";
-
-    /**
      * Extra containing a single {@link Uri} indicating the location of the successfully
      * downloaded file. Set on the intent provided via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
      * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
      * {@link #RESULT_SUCCESSFUL}.
-     * TODO: Not systemapi.
      */
     public static final String EXTRA_COMPLETED_FILE_URI =
             "android.telephony.mbms.extra.COMPLETED_FILE_URI";
 
     public static final int RESULT_SUCCESSFUL = 1;
-    public static final int RESULT_CANCELLED  = 2;
-    public static final int RESULT_EXPIRED    = 3;
+    public static final int RESULT_CANCELLED = 2;
+    public static final int RESULT_EXPIRED = 3;
+    public static final int RESULT_IO_ERROR = 4;
     // TODO - more results!
 
     /** @hide */
@@ -207,8 +99,16 @@
     public static final int STATUS_PENDING_REPAIR = 3;
     public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
 
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
     private final Context mContext;
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
     private final MbmsDownloadManagerCallback mCallback;
@@ -236,10 +136,21 @@
      *
      * Note that this call will bind a remote service and that may take a bit. The instance of
      * {@link MbmsDownloadManager} that is returned will not be ready for use until
-     * {@link IMbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+     * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
      * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
      *
-     * This also may throw an {@link IllegalArgumentException} or a {@link MbmsException}.
+     * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+     *
+     * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this
+     * method while there is an active instance of {@link MbmsDownloadManager} in your process
+     * (in other words, one that has not had {@link #dispose()} called on it), this method will
+     * throw an {@link MbmsException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsDownloadManagerCallback#error(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call dispose() on the instance of
+     * {@link MbmsDownloadManager} that you received before calling this method again.
      *
      * @param context The instance of {@link Context} to use
      * @param listener A callback to get asynchronous error messages and file service updates.
@@ -249,8 +160,16 @@
     public static MbmsDownloadManager create(Context context,
             MbmsDownloadManagerCallback listener, int subscriptionId)
             throws MbmsException {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+        }
         MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
-        mdm.bindAndInitialize();
+        try {
+            mdm.bindAndInitialize();
+        } catch (MbmsException e) {
+            sIsInitialized.set(false);
+            throw e;
+        }
         return mdm;
     }
 
@@ -266,16 +185,27 @@
                             result = downloadService.initialize(mSubscriptionId, mCallback);
                         } catch (RemoteException e) {
                             Log.e(LOG_TAG, "Service died before initialization");
+                            sIsInitialized.set(false);
                             return;
                         } catch (RuntimeException e) {
                             Log.e(LOG_TAG, "Runtime exception during initialization");
-                            mCallback.error(
+                            sendErrorToApp(
                                     MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                     e.toString());
+                            sIsInitialized.set(false);
                             return;
                         }
                         if (result != MbmsException.SUCCESS) {
-                            mCallback.error(result, "Error returned during initialization");
+                            sendErrorToApp(result, "Error returned during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        try {
+                            downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+                        } catch (RemoteException e) {
+                            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+                                    "Middleware lost during initialization");
+                            sIsInitialized.set(false);
                             return;
                         }
                         mService.set(downloadService);
@@ -283,6 +213,7 @@
 
                     @Override
                     public void onServiceDisconnected(ComponentName name) {
+                        sIsInitialized.set(false);
                         mService.set(null);
                     }
                 });
@@ -292,7 +223,7 @@
      * An inspection API to retrieve the list of available
      * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
      * The results are returned asynchronously via a call to
-     * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+     * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)}
      *
      * The serviceClasses argument lets the app filter on types of programming and is opaque data
      * negotiated beforehand between the app and the carrier.
@@ -306,7 +237,7 @@
      * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
      *
      * @param classList A list of service classes which the app wishes to receive
-     *                  {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
+     *                  {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
      *                  about. Subsequent calls to this method will replace this list of service
      *                  classes (i.e. the middleware will no longer send updates for services
      *                  matching classes only in the old list).
@@ -336,14 +267,15 @@
      * local instance of {@link android.content.SharedPreferences} and by the middleware.
      *
      * If this method is not called at least once before calling
-     * {@link #download(DownloadRequest, IDownloadCallback)}, the framework
+     * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework
      * will default to a directory formed by the concatenation of the app's files directory and
      * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
      *
      * Before calling this method, the app must cancel all of its pending
      * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
      * an {@link MbmsException} will be thrown with code
-     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+     * provided directory is the same as what has been previously configured.
      *
      * The {@link File} supplied as a root temp file directory must already exist. If not, an
      * {@link IllegalArgumentException} will be thrown.
@@ -384,6 +316,26 @@
     }
 
     /**
+     * Retrieves the currently configured temp file root directory. Returns the file that was
+     * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+     * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting
+     * the temp file root. If neither method has been called since the last time the app's shared
+     * preferences were reset, returns null.
+     *
+     * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+     *         configured.
+     */
+    public @Nullable File getTempFileRootDirectory() {
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+        if (path != null) {
+            return new File(path);
+        }
+        return null;
+    }
+
+    /**
      * Requests a download of a file that is available via multicast.
      *
      * downloadListener is an optional callback object which can be used to get progress reports
@@ -404,7 +356,7 @@
      * @param progressListener Optional listener that will be provided progress updates
      *                         if the app is running.
      */
-    public void download(DownloadRequest request, IDownloadProgressListener progressListener)
+    public void download(DownloadRequest request, DownloadProgressListener progressListener)
             throws MbmsException {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
@@ -434,7 +386,7 @@
     /**
      * Returns a list of pending {@link DownloadRequest}s that originated from this application.
      * A pending request is one that was issued via
-     * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+     * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * @return A list, possibly empty, of {@link DownloadRequest}s
      */
@@ -550,43 +502,15 @@
                 return;
             }
             downloadService.dispose(mSubscriptionId);
-            mService.set(null);
         } catch (RemoteException e) {
             // Ignore
             Log.i(LOG_TAG, "Remote exception while disposing of service");
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
         }
     }
 
-    /**
-     * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
-     * the various intents from the middleware should be targeted towards.
-     * @param uid The uid of the frontend app.
-     * @return The component name of the receiver that the middleware should send its intents to,
-     * or null if the app didn't declare it in the manifest.
-     *
-     * @hide
-     * future systemapi
-     */
-    public static ComponentName getAppReceiverFromUid(Context context, int uid) {
-        String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
-        if (packageNames == null) {
-            return null;
-        }
-
-        for (String packageName : packageNames) {
-            ComponentName candidate = new ComponentName(packageName,
-                    MbmsDownloadReceiver.class.getCanonicalName());
-            Intent queryIntent = new Intent();
-            queryIntent.setComponent(candidate);
-            List<ResolveInfo> receivers =
-                    context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
-            if (receivers != null && receivers.size() > 0) {
-                return candidate;
-            }
-        }
-        return null;
-    }
-
     private void writeDownloadRequestToken(DownloadRequest request) {
         File token = getDownloadRequestTokenPath(request);
         if (!token.getParentFile().exists()) {
@@ -651,4 +575,12 @@
             }
         }
     }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mCallback.error(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index 911f83f..7a6631a 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -16,11 +16,17 @@
 
 package android.telephony;
 
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
+import android.telephony.mbms.InternalStreamingManagerCallback;
+import android.telephony.mbms.InternalStreamingServiceCallback;
 import android.telephony.mbms.MbmsException;
 import android.telephony.mbms.MbmsStreamingManagerCallback;
 import android.telephony.mbms.MbmsUtils;
@@ -31,6 +37,7 @@
 import android.util.Log;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -41,21 +48,42 @@
  */
 public class MbmsStreamingManager {
     private static final String LOG_TAG = "MbmsStreamingManager";
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS streaming
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String MBMS_STREAMING_SERVICE_ACTION =
             "android.telephony.action.EmbmsStreaming";
 
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
     private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
-    private MbmsStreamingManagerCallback mCallbackToApp;
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
+
+    private InternalStreamingManagerCallback mInternalCallback;
 
     private final Context mContext;
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
     private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
-                    int subscriptionId) {
+                    int subscriptionId, Handler handler) {
         mContext = context;
-        mCallbackToApp = callback;
         mSubscriptionId = subscriptionId;
+        if (handler == null) {
+            handler = new Handler(Looper.getMainLooper());
+        }
+        mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
     }
 
     /**
@@ -65,27 +93,62 @@
      * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
      * during the initialization or binding process.
      *
+     *
+     * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
+     * method while there is an active instance of {@link MbmsStreamingManager} in your process
+     * (in other words, one that has not had {@link #dispose()} called on it), this method will
+     * throw an {@link MbmsException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsStreamingManagerCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call dispose() on the instance of
+     * {@link MbmsStreamingManager} that you received before calling this method again.
+     *
      * @param context The {@link Context} to use.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
      * @param subscriptionId The subscription ID to use.
+     * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
+     *                processed on the main looper (in other words, the looper returned from
+     *                {@link Looper#getMainLooper()}).
      */
     public static MbmsStreamingManager create(Context context,
-            MbmsStreamingManagerCallback callback, int subscriptionId)
+            MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
             throws MbmsException {
-        MbmsStreamingManager manager = new MbmsStreamingManager(context, callback, subscriptionId);
-        manager.bindAndInitialize();
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+        }
+        MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
+                subscriptionId, handler);
+        try {
+            manager.bindAndInitialize();
+        } catch (MbmsException e) {
+            sIsInitialized.set(false);
+            throw e;
+        }
         return manager;
     }
 
     /**
      * Create a new MbmsStreamingManager using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingManagerCallback, int)}.
+     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+     */
+    public static MbmsStreamingManager create(Context context,
+            MbmsStreamingManagerCallback callback, Handler handler)
+            throws MbmsException {
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+    }
+
+    /**
+     * Create a new MbmsStreamingManager using the system default data subscription ID and
+     * default {@link Handler}.
+     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
      */
     public static MbmsStreamingManager create(Context context,
             MbmsStreamingManagerCallback callback)
             throws MbmsException {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId());
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
     }
 
     /**
@@ -95,17 +158,19 @@
      * May throw an {@link IllegalStateException}
      */
     public void dispose() {
-        IMbmsStreamingService streamingService = mService.get();
-        if (streamingService == null) {
-            // Ignore and return, assume already disposed.
-            return;
-        }
         try {
+            IMbmsStreamingService streamingService = mService.get();
+            if (streamingService == null) {
+                // Ignore and return, assume already disposed.
+                return;
+            }
             streamingService.dispose(mSubscriptionId);
         } catch (RemoteException e) {
             // Ignore for now
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
         }
-        mService.set(null);
     }
 
     /**
@@ -139,16 +204,17 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
+            sIsInitialized.set(false);
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
     /**
-     * Starts streaming a requested service, reporting status to the indicated listener.
+     * Starts streaming a requested service, reporting status to the indicated callback.
      * Returns an object used to control that stream. The stream may not be ready for consumption
      * immediately upon return from this method -- wait until the streaming state has been
      * reported via
-     * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)}
+     * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
      *
      * May throw an
      * {@link MbmsException} containing any of the error codes in
@@ -158,34 +224,44 @@
      *
      * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
-     * Asynchronous errors through the listener include any of the errors in
+     * Asynchronous errors through the callback include any of the errors in
      * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
      * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
      *
      * @param serviceInfo The information about the service to stream.
-     * @param listener A listener that'll be called when something about the stream changes.
+     * @param callback A callback that'll be called when something about the stream changes.
+     * @param handler A handler that calls to {@code callback} should be called on. If null,
+     *                defaults to the handler provided via
+     *                {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
      * @return An instance of {@link StreamingService} through which the stream can be controlled.
      */
     public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            StreamingServiceCallback listener) throws MbmsException {
+            StreamingServiceCallback callback, Handler handler) throws MbmsException {
         IMbmsStreamingService streamingService = mService.get();
         if (streamingService == null) {
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
         }
 
+        InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
+                callback, handler == null ? mInternalCallback.getHandler() : handler);
+
+        StreamingService serviceForApp = new StreamingService(
+                mSubscriptionId, streamingService, serviceInfo, serviceCallback);
+
         try {
             int returnCode = streamingService.startStreaming(
-                    mSubscriptionId, serviceInfo.getServiceId(), listener);
+                    mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
             if (returnCode != MbmsException.SUCCESS) {
                 throw new MbmsException(returnCode);
             }
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
+            sIsInitialized.set(false);
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
 
-        return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener);
+        return serviceForApp;
     }
 
     private void bindAndInitialize() throws MbmsException {
@@ -197,19 +273,34 @@
                                 IMbmsStreamingService.Stub.asInterface(service);
                         int result;
                         try {
-                            result = streamingService.initialize(mCallbackToApp, mSubscriptionId);
+                            result = streamingService.initialize(mInternalCallback,
+                                    mSubscriptionId);
                         } catch (RemoteException e) {
                             Log.e(LOG_TAG, "Service died before initialization");
+                            sendErrorToApp(
+                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
                             return;
                         } catch (RuntimeException e) {
                             Log.e(LOG_TAG, "Runtime exception during initialization");
-                            mCallbackToApp.error(
+                            sendErrorToApp(
                                     MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                     e.toString());
+                            sIsInitialized.set(false);
                             return;
                         }
                         if (result != MbmsException.SUCCESS) {
-                            mCallbackToApp.error(result, "Error returned during initialization");
+                            sendErrorToApp(result, "Error returned during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        try {
+                            streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+                        } catch (RemoteException e) {
+                            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+                                    "Middleware lost during initialization");
+                            sIsInitialized.set(false);
                             return;
                         }
                         mService.set(streamingService);
@@ -217,8 +308,17 @@
 
                     @Override
                     public void onServiceDisconnected(ComponentName name) {
+                        sIsInitialized.set(false);
                         mService.set(null);
                     }
                 });
     }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mInternalCallback.error(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
index d6bd5dc..d91e9ad 100644
--- a/telephony/java/android/telephony/mbms/DownloadProgressListener.java
+++ b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
@@ -16,6 +16,8 @@
 
 package android.telephony.mbms;
 
+import android.os.RemoteException;
+
 /**
  * A optional listener class used by download clients to track progress.
  * @hide
@@ -38,8 +40,9 @@
      * @param currentDecodedSize is the number of bytes that have been decoded.
      * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
      */
+    @Override
     public void progress(DownloadRequest request, FileInfo fileInfo,
             int currentDownloadSize, int fullDownloadSize,
-            int currentDecodedSize, int fullDecodedSize) {
+            int currentDecodedSize, int fullDecodedSize) throws RemoteException {
     }
 }
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 01e0bbd..eae9011 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -25,6 +25,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -77,12 +78,18 @@
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+        /**
+         * Sets the service from which the download request to be built will download from.
+         * @param serviceInfo
+         * @return
+         */
         public Builder setServiceInfo(FileServiceInfo serviceInfo) {
             fileServiceId = serviceInfo.getServiceId();
             return this;
         }
 
         /**
+         * Set the service ID for the download request. For use by the middleware only.
          * @hide
          * TODO: systemapi
          */
@@ -91,11 +98,23 @@
             return this;
         }
 
+        /**
+         * Sets the source URI for the download request to be built.
+         * @param source
+         * @return
+         */
         public Builder setSource(Uri source) {
             this.source = source;
             return this;
         }
 
+        /**
+         * Sets the destination URI for the download request to be built. The middleware should
+         * not set this directly.
+         * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
+         *             final destination of the download.
+         * @return
+         */
         public Builder setDest(Uri dest) {
             if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
                 throw new IllegalArgumentException("Destination uri must not exceed length " +
@@ -105,11 +124,25 @@
             return this;
         }
 
-        public Builder setSubscriptionId(int sub) {
-            this.subscriptionId = sub;
+        /**
+         * Set the subscription ID on which the file(s) should be downloaded.
+         * @param subscriptionId
+         * @return
+         */
+        public Builder setSubscriptionId(int subscriptionId) {
+            this.subscriptionId = subscriptionId;
             return this;
         }
 
+        /**
+         * Set the {@link Intent} that should be sent when the download completes or fails. This
+         * should be an intent with a explicit {@link android.content.ComponentName} targeted to a
+         * {@link android.content.BroadcastReceiver} in the app's package.
+         *
+         * The middleware should not use this method.
+         * @param intent
+         * @return
+         */
         public Builder setAppIntent(Intent intent) {
             this.appIntent = intent.toUri(0);
             if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
@@ -120,7 +153,12 @@
         }
 
         /**
-         * For use by middleware only
+         * For use by the middleware to set the byte array of opaque data. The opaque data
+         * includes information about the download request that is used by the client app and the
+         * manager code, but is irrelevant to the middleware.
+         * @param data A byte array, the contents of which should have been originally obtained
+         *             from {@link DownloadRequest#getOpaqueData()}.
+         * @return
          * TODO: systemapi
          * @hide
          */
@@ -201,22 +239,40 @@
         out.writeInt(version);
     }
 
+    /**
+     * @return The ID of the file service to download from.
+     */
     public String getFileServiceId() {
         return fileServiceId;
     }
 
+    /**
+     * @return The source URI to download from
+     */
     public Uri getSourceUri() {
         return sourceUri;
     }
 
+    /**
+     * For use by the client app only.
+     * @return The URI of the final destination of the download.
+     */
     public Uri getDestinationUri() {
         return destinationUri;
     }
 
+    /**
+     * @return The subscription ID on which to perform MBMS operations.
+     */
     public int getSubscriptionId() {
         return subscriptionId;
     }
 
+    /**
+     * For internal use -- returns the intent to send to the app after download completion or
+     * failure.
+     * @hide
+     */
     public Intent getIntentForApp() {
         try {
             return Intent.parseUri(serializedResultIntentForApp, 0);
@@ -226,6 +282,10 @@
     }
 
     /**
+     * For use by the middleware only. The byte array returned from this method should be
+     * persisted and sent back to the app upon download completion or failure by passing it into
+     * {@link Builder#setOpaqueData(byte[])}.
+     * @return A byte array of opaque data to persist.
      * @hide
      * TODO: systemapi
      */
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 1b87393..f97131d 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -38,16 +38,6 @@
      */
     private final String mimeType;
 
-    /**
-     * The size of the file in bytes.
-     */
-    private final long size;
-
-    /**
-     * The MD5 hash of the file.
-     */
-    private final byte md5Hash[];
-
     public static final Parcelable.Creator<FileInfo> CREATOR =
             new Parcelable.Creator<FileInfo>() {
         @Override
@@ -61,29 +51,24 @@
         }
     };
 
-    public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) {
+    /**
+     * @hide
+     * TODO: systemapi
+     */
+    public FileInfo(Uri uri, String mimeType) {
         this.uri = uri;
         this.mimeType = mimeType;
-        this.size = size;
-        this.md5Hash = md5Hash;
     }
 
     private FileInfo(Parcel in) {
         uri = in.readParcelable(null);
         mimeType = in.readString();
-        size = in.readLong();
-        int arraySize = in.readInt();
-        md5Hash = new byte[arraySize];
-        in.readByteArray(md5Hash);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(uri, flags);
         dest.writeString(mimeType);
-        dest.writeLong(size);
-        dest.writeInt(md5Hash.length);
-        dest.writeByteArray(md5Hash);
     }
 
     @Override
@@ -98,12 +83,4 @@
     public String getMimeType() {
         return mimeType;
     }
-
-    public long getSize() {
-        return size;
-    }
-
-    public byte[] getMd5Hash() {
-        return md5Hash;
-    }
 }
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 6646dc8..8afe4d3 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -32,6 +32,7 @@
 public class FileServiceInfo extends ServiceInfo implements Parcelable {
     private final List<FileInfo> files;
 
+    /** @hide TODO: systemapi */
     public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
             List<Locale> newLocales, String newServiceId, Date start, Date end,
             List<FileInfo> newFiles) {
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java
new file mode 100644
index 0000000..b52df8c
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+
+import java.util.List;
+
+/** @hide */
+public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
+    private final Handler mHandler;
+    private final MbmsStreamingManagerCallback mAppCallback;
+
+    public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+            Handler handler) {
+        mAppCallback = appCallback;
+        mHandler = handler;
+    }
+
+    @Override
+    public void error(int errorCode, String message) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onError(errorCode, message);
+            }
+        });
+    }
+
+    @Override
+    public void streamingServicesUpdated(List<StreamingServiceInfo> services)
+            throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onStreamingServicesUpdated(services);
+            }
+        });
+    }
+
+    @Override
+    public void middlewareReady() throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onMiddlewareReady();
+            }
+        });
+    }
+
+    public Handler getHandler() {
+        return mHandler;
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
new file mode 100644
index 0000000..bb337b2
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/** @hide */
+public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
+    private final StreamingServiceCallback mAppCallback;
+    private final Handler mHandler;
+
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+        mAppCallback = appCallback;
+        mHandler = handler;
+    }
+
+    @Override
+    public void error(int errorCode, String message) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onError(errorCode, message);
+            }
+        });
+    }
+
+    @Override
+    public void streamStateUpdated(int state, int reason) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onStreamStateUpdated(state, reason);
+            }
+        });
+    }
+
+    @Override
+    public void mediaDescriptionUpdated() throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onMediaDescriptionUpdated();
+            }
+        });
+    }
+
+    @Override
+    public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+            }
+        });
+    }
+
+    @Override
+    public void streamMethodUpdated(int methodType) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onStreamMethodUpdated(methodType);
+            }
+        });
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index ba25f66..17291d0 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -16,6 +16,9 @@
 
 package android.telephony.mbms;
 
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadManager;
+
 import java.util.List;
 
 /**
@@ -24,12 +27,8 @@
  */
 public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
 
-    public final static int ERROR_CARRIER_NOT_SUPPORTED      = 1;
-    public final static int ERROR_UNABLE_TO_INITIALIZE       = 2;
-    public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY  = 3;
-
-
-    public void error(int errorCode, String message) {
+    @Override
+    public void error(int errorCode, String message) throws RemoteException {
         // default implementation empty
     }
 
@@ -45,7 +44,8 @@
      * @param services a List of FileServiceInfos
      *
      */
-    public void fileServicesUpdated(List<FileServiceInfo> services) {
+    @Override
+    public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
         // default implementation empty
     }
 
@@ -58,7 +58,7 @@
      * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
     @Override
-    public void middlewareReady() {
+    public void middlewareReady() throws RemoteException {
         // default implementation empty
     }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 339ff39..ba7d120 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.MbmsDownloadManager;
+import android.telephony.mbms.vendor.VendorIntents;
 import android.util.Log;
 
 import java.io.File;
@@ -56,9 +57,9 @@
     /**
      * Indicates that the intent sent had an invalid action. This will be the result if
      * {@link Intent#getAction()} returns anything other than
-     * {@link MbmsDownloadManager#ACTION_DOWNLOAD_RESULT_INTERNAL},
-     * {@link MbmsDownloadManager#ACTION_FILE_DESCRIPTOR_REQUEST}, or
-     * {@link MbmsDownloadManager#ACTION_CLEANUP}.
+     * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL},
+     * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+     * {@link VendorIntents#ACTION_CLEANUP}.
      * This is a fatal result code and no result extras should be expected.
      */
     public static final int RESULT_INVALID_ACTION = 1;
@@ -70,7 +71,7 @@
     public static final int RESULT_MALFORMED_INTENT = 2;
 
     /**
-     * Indicates that the supplied value for {@link MbmsDownloadManager#EXTRA_TEMP_FILE_ROOT}
+     * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT}
      * does not match what the app has stored.
      * This is a fatal result code and no result extras should be expected.
      */
@@ -104,18 +105,18 @@
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
         }
-        if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT),
+        if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT),
                 MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
             setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
             return;
         }
 
-        if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+        if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
             moveDownloadedFile(context, intent);
             cleanupPostMove(context, intent);
-        } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+        } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
             generateTempFiles(context, intent);
-        } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) {
+        } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
             cleanupTempFiles(context, intent);
         } else {
             setResultCode(RESULT_INVALID_ACTION);
@@ -123,16 +124,16 @@
     }
 
     private boolean verifyIntentContents(Context context, Intent intent) {
-        if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+        if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
             if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_REQUEST)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) {
                 Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
@@ -141,12 +142,12 @@
                         "Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FINAL_URI)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) {
                 Log.w(LOG_TAG, "Download result did not include the path to the final " +
                         "temp file. Ignoring.");
                 return false;
             }
-            DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+            DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
             String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
             File expectedTokenFile = new File(
                     MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
@@ -156,27 +157,27 @@
                         "Expected " + expectedTokenFile);
                 return false;
             }
-        } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) {
+        } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
                 Log.w(LOG_TAG, "Temp file request did not include the associated service info." +
                         " Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
-        } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) {
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) {
+        } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
                 Log.w(LOG_TAG, "Cleanup request did not include the associated service info." +
                         " Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE)) {
+            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) {
                 Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
                         "Ignoring.");
                 return false;
@@ -186,7 +187,7 @@
     }
 
     private void moveDownloadedFile(Context context, Intent intent) {
-        DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+        DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
         Intent intentForApp = request.getIntentForApp();
 
         int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
@@ -200,7 +201,7 @@
         }
 
         Uri destinationUri = request.getDestinationUri();
-        Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI);
+        Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI);
         if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
             Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -225,13 +226,13 @@
     }
 
     private void cleanupPostMove(Context context, Intent intent) {
-        DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+        DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
         if (request == null) {
             Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
             return;
         }
 
-        List<Uri> tempFiles = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_TEMP_LIST);
+        List<Uri> tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST);
         if (tempFiles == null) {
             return;
         }
@@ -246,15 +247,15 @@
 
     private void generateTempFiles(Context context, Intent intent) {
         FileServiceInfo serviceInfo =
-                intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
+                intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
         if (serviceInfo == null) {
             Log.w(LOG_TAG, "Temp file request did not include the associated service info. " +
                     "Ignoring.");
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
         }
-        int fdCount = intent.getIntExtra(MbmsDownloadManager.EXTRA_FD_COUNT, 0);
-        List<Uri> pausedList = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_PAUSED_LIST);
+        int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0);
+        List<Uri> pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST);
 
         if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
             Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -269,8 +270,8 @@
                 generateUrisForPausedFiles(context, serviceInfo, pausedList);
 
         Bundle result = new Bundle();
-        result.putParcelableArrayList(MbmsDownloadManager.EXTRA_FREE_URI_LIST, freshTempFiles);
-        result.putParcelableArrayList(MbmsDownloadManager.EXTRA_PAUSED_URI_LIST, pausedFiles);
+        result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles);
+        result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles);
         setResultCode(RESULT_OK);
         setResultExtras(result);
     }
@@ -353,11 +354,11 @@
 
     private void cleanupTempFiles(Context context, Intent intent) {
         FileServiceInfo serviceInfo =
-                intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
+                intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
         File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
                 serviceInfo.getServiceId());
         final List<Uri> filesInUse =
-                intent.getParcelableArrayListExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE);
+                intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE);
         File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
             @Override
             public boolean accept(File file) {
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 8888119..7cf8792 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -31,7 +31,7 @@
 
     /**
      * Indicates that the app attempted to perform an operation on an instance of
-     * {@link android.telephony.MbmsDownloadManager} or
+     * TODO: link android.telephony.MbmsDownloadManager or
      * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
      */
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
@@ -44,10 +44,11 @@
      * middleware. They are applicable to both streaming and file-download use-cases.
      */
     public static class InitializationErrors {
+        private InitializationErrors() {}
         /**
          * Indicates that the app tried to create more than one instance each of
          * {@link android.telephony.MbmsStreamingManager} or
-         * {@link android.telephony.MbmsDownloadManager}.
+         * TODO: link android.telephony.MbmsDownloadManager
          */
         public static final int ERROR_DUPLICATE_INITIALIZE = 101;
         /** Indicates that the app is not authorized to access media via MBMS.*/
@@ -61,10 +62,11 @@
      * streaming and file-download.
      */
     public static class GeneralErrors {
+        private GeneralErrors() {}
         /**
          * Indicates that the app attempted to perform an operation before receiving notification
-         * that the middleware is ready via {@link MbmsStreamingManagerCallback#middlewareReady()}
-         * or {@link MbmsDownloadManagerCallback#middlewareReady()}.
+         * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()}
+         * or TODO: link MbmsDownloadManagerCallback#middlewareReady
          */
         public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
         /**
@@ -97,6 +99,7 @@
      * Indicates the errors that are applicable only to the streaming use-case
      */
     public static class StreamingErrors {
+        private StreamingErrors() {}
         /** Indicates that the middleware cannot start a stream due to too many ongoing streams */
         public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301;
 
@@ -105,7 +108,8 @@
 
         /**
          * Indicates that the app called
-         * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+         * {@link android.telephony.MbmsStreamingManager#startStreaming(
+         * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
          * more than once for the same {@link StreamingServiceInfo}.
          */
         public static final int ERROR_DUPLICATE_START_STREAM = 303;
@@ -113,6 +117,8 @@
 
     /**
      * Indicates the errors that are applicable only to the file-download use-case
+     * TODO: unhide
+     * @hide
      */
     public static class DownloadErrors {
         /**
@@ -127,9 +133,7 @@
 
     private final int mErrorCode;
 
-    /** @hide
-     * TODO: future systemapi
-     */
+    /** @hide */
     public MbmsException(int errorCode) {
         super();
         mErrorCode = errorCode;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
index 2e91be9..831050e 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -16,20 +16,26 @@
 
 package android.telephony.mbms;
 
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.MbmsStreamingManager;
+
 import java.util.List;
 
 /**
- * A Parcelable class with Cell-Broadcast service information.
+ * A callback class that is used to receive information from the middleware on MBMS streaming
+ * services. An instance of this object should be passed into
+ * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}.
  * @hide
  */
-public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
-
-    public final static int ERROR_CARRIER_NOT_SUPPORTED      = 1;
-    public final static int ERROR_UNABLE_TO_INITIALIZE       = 2;
-    public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY  = 3;
-
-
-    public void error(int errorCode, String message) {
+public class MbmsStreamingManagerCallback {
+    /**
+     * Called by the middleware when it has detected an error condition. The possible error codes
+     * are listed in {@link MbmsException}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(int errorCode, String message) {
         // default implementation empty
     }
 
@@ -45,7 +51,7 @@
      * @param services a List of StreamingServiceInfos
      *
      */
-    public void streamingServicesUpdated(List<StreamingServiceInfo> services) {
+    public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
         // default implementation empty
     }
 
@@ -57,8 +63,7 @@
      * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
      * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
-    @Override
-    public void middlewareReady() {
+    public void onMiddlewareReady() {
         // default implementation empty
     }
 }
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index f9ad44c..423ae01 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -30,43 +30,22 @@
 import java.util.Set;
 
 /**
- * A Parcelable class with Cell-Broadcast service information.
+ * Describes a cell-broadcast service. This class should not be instantiated directly -- use
+ * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden
  * @hide
  */
-public class ServiceInfo implements Parcelable {
+public class ServiceInfo {
     // arbitrary limit on the number of locale -> name pairs we support
     final static int MAP_LIMIT = 1000;
-    /**
-     * User displayable names listed by language.  Unmodifiable.
-     */
-    final Map<Locale, String> names;
 
-    /**
-     * The class name for this service - used to catagorize and filter
-     */
-    final String className;
+    private final Map<Locale, String> names;
+    private final String className;
+    private final List<Locale> locales;
+    private final String serviceId;
+    private final Date sessionStartTime;
+    private final Date sessionEndTime;
 
-    /**
-     * The languages available for this service content
-     */
-    final List<Locale> locales;
-
-    /**
-     * The carrier's identifier for the service.
-     */
-    final String serviceId;
-
-    /**
-     * The start time indicating when this service will be available.
-     */
-    final Date sessionStartTime;
-
-    /**
-     * The end time indicating when this sesion stops being available.
-     */
-    final Date sessionEndTime;
-
-
+    /** @hide */
     public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
             String newServiceId, Date start, Date end) {
         if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName)
@@ -89,20 +68,8 @@
         sessionEndTime = (Date)end.clone();
     }
 
-    public static final Parcelable.Creator<FileServiceInfo> CREATOR =
-            new Parcelable.Creator<FileServiceInfo>() {
-        @Override
-        public FileServiceInfo createFromParcel(Parcel source) {
-            return new FileServiceInfo(source);
-        }
-
-        @Override
-        public FileServiceInfo[] newArray(int size) {
-            return new FileServiceInfo[size];
-        }
-    };
-
-    ServiceInfo(Parcel in) {
+    /** @hide */
+    protected ServiceInfo(Parcel in) {
         int mapCount = in.readInt();
         if (mapCount > MAP_LIMIT || mapCount < 0) {
             throw new RuntimeException("bad map length" + mapCount);
@@ -128,7 +95,7 @@
         sessionEndTime = (java.util.Date) in.readSerializable();
     }
 
-    @Override
+    /** @hide */
     public void writeToParcel(Parcel dest, int flags) {
         Set<Locale> keySet = names.keySet();
         dest.writeInt(keySet.size());
@@ -147,31 +114,44 @@
         dest.writeSerializable(sessionEndTime);
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
+    /**
+     * User displayable names listed by language. Do not modify the map returned from this method.
+     */
     public Map<Locale, String> getNames() {
         return names;
     }
 
+    /**
+     * The class name for this service - used to categorize and filter
+     */
     public String getClassName() {
         return className;
     }
 
+    /**
+     * The languages available for this service content
+     */
     public List<Locale> getLocales() {
         return locales;
     }
 
+    /**
+     * The carrier's identifier for the service.
+     */
     public String getServiceId() {
         return serviceId;
     }
 
+    /**
+     * The start time indicating when this service will be available.
+     */
     public Date getSessionStartTime() {
         return sessionStartTime;
     }
 
+    /**
+     * The end time indicating when this session stops being available.
+     */
     public Date getSessionEndTime() {
         return sessionEndTime;
     }
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index c49f8a9..5c4b786 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -26,13 +26,17 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * Class used to represent a single MBMS stream. After a stream has been started with
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * StreamingServiceCallback, android.os.Handler)},
+ * this class is used to hold information about the stream and control it.
  * @hide
  */
 public class StreamingService {
     private static final String LOG_TAG = "MbmsStreamingService";
 
     /**
-     * The state of a stream, reported via {@link StreamingServiceCallback#streamStateUpdated}
+     * The state of a stream, reported via {@link StreamingServiceCallback#onStreamStateUpdated}
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -44,7 +48,7 @@
 
     /**
      * The reason for a stream state change, reported via
-     * {@link StreamingServiceCallback#streamStateUpdated}
+     * {@link StreamingServiceCallback#onStreamStateUpdated}
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -60,7 +64,8 @@
 
     /**
      * State changed due to a call to {@link #stopStreaming()} or
-     * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+     * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+     * StreamingServiceCallback, android.os.Handler)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
 
@@ -87,27 +92,28 @@
     /**
      * State changed due to the device leaving the where this stream is being broadcast.
      */
-    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 5;
+    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
 
     /**
      * The method of transmission currently used for a stream,
-     * reported via {@link StreamingServiceCallback#streamMethodUpdated}
+     * reported via {@link StreamingServiceCallback#onStreamMethodUpdated}
      */
     public final static int BROADCAST_METHOD = 1;
     public final static int UNICAST_METHOD   = 2;
 
     private final int mSubscriptionId;
     private final StreamingServiceInfo mServiceInfo;
-    private final IStreamingServiceCallback mCallback;
+    private final InternalStreamingServiceCallback mCallback;
 
     private IMbmsStreamingService mService;
+
     /**
      * @hide
      */
     public StreamingService(int subscriptionId,
             IMbmsStreamingService service,
             StreamingServiceInfo streamingServiceInfo,
-            IStreamingServiceCallback callback) {
+            InternalStreamingServiceCallback callback) {
         mSubscriptionId = subscriptionId;
         mService = service;
         mServiceInfo = streamingServiceInfo;
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index cab9c23..eeef8bc 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -17,10 +17,11 @@
 package android.telephony.mbms;
 
 /**
- * A Callback class for use when the application is actively streaming content.
+ * A callback class for use when the application is actively streaming content. The middleware
+ * will provide updates on the status of the stream via this callback.
  * @hide
  */
-public class StreamingServiceCallback extends IStreamingServiceCallback.Stub {
+public class StreamingServiceCallback {
 
     /**
      * Indicates broadcast signal strength is not available for this service.
@@ -31,8 +32,13 @@
      */
     public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
-    @Override
-    public void error(int errorCode, String message) {
+    /**
+     * Called by the middleware when it has detected an error condition in this stream. The
+     * possible error codes are listed in {@link MbmsException}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(int errorCode, String message) {
         // default implementation empty
     }
 
@@ -42,8 +48,7 @@
      * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
      * and {@link StreamingService#STATE_STALLED}.
      */
-    @Override
-    public void streamStateUpdated(@StreamingService.StreamingState int state,
+    public void onStreamStateUpdated(@StreamingService.StreamingState int state,
             @StreamingService.StreamingStateChangeReason int reason) {
         // default implementation empty
     }
@@ -58,8 +63,7 @@
      * This may be called when a looping stream hits the end or
      * when parameters have changed to account for time drift.
      */
-    @Override
-    public void mediaDescriptionUpdated() {
+    public void onMediaDescriptionUpdated() {
         // default implementation empty
     }
 
@@ -73,8 +77,7 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this service due to timing, geography or popularity.
      */
-    @Override
-    public void broadcastSignalStrengthUpdated(int signalStrength) {
+    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
         // default implementation empty
     }
 
@@ -94,8 +97,7 @@
      * See {@link StreamingService#BROADCAST_METHOD} and
      * {@link StreamingService#UNICAST_METHOD}
      */
-    @Override
-    public void streamMethodUpdated(int methodType) {
+    public void onStreamMethodUpdated(int methodType) {
         // default implementation empty
     }
 }
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
index 77ce3bb..8e7917a 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,15 +26,25 @@
 import java.util.Map;
 
 /**
- * A Parcelable class Cell-Broadcast media stream information.
- * This may not have any more info than ServiceInfo, but kept for completeness.
+ * Describes a single MBMS streaming service.
  * @hide
  */
-public class StreamingServiceInfo extends ServiceInfo implements Parcelable {
+public final class StreamingServiceInfo extends ServiceInfo implements Parcelable {
 
-    public StreamingServiceInfo(Map<Locale, String> newNames, String newClassName,
-            List<Locale> newLocales, String newServiceId, Date start, Date end) {
-        super(newNames, newClassName, newLocales, newServiceId, start, end);
+    /**
+     * @param names User displayable names listed by language.
+     * @param className The class name for this service - used by frontend apps to categorize and
+     *                  filter.
+     * @param locales The languages available for this service content.
+     * @param serviceId The carrier's identifier for the service.
+     * @param start The start time indicating when this service will be available.
+     * @param end The end time indicating when this session stops being available.
+     * @hide
+     */
+    @SystemApi
+    public StreamingServiceInfo(Map<Locale, String> names, String className,
+            List<Locale> locales, String serviceId, Date start, Date end) {
+        super(names, className, locales, serviceId, start, end);
     }
 
     public static final Parcelable.Creator<StreamingServiceInfo> CREATOR =
@@ -49,7 +60,7 @@
         }
     };
 
-    StreamingServiceInfo(Parcel in) {
+    private StreamingServiceInfo(Parcel in) {
         super(in);
     }
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index edd5858..71713d0 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -18,10 +18,13 @@
 
 import android.annotation.NonNull;
 import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
 import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.FileServiceInfo;
 import android.telephony.mbms.IDownloadProgressListener;
 import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsException;
 
 import java.util.List;
@@ -44,16 +47,40 @@
      * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
      * {@link IMbmsDownloadManagerCallback#error(int, String)}.
      *
-     * @param listener The callback to use to communicate with the app.
+     * @param callback The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
      */
-    @Override
-    public int initialize(int subscriptionId,
-            IMbmsDownloadManagerCallback listener) throws RemoteException {
+    public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+            throws RemoteException {
         return 0;
     }
 
     /**
+     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int initialize(int subscriptionId,
+            final IMbmsDownloadManagerCallback callback) throws RemoteException {
+        return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+            @Override
+            public void error(int errorCode, String message) throws RemoteException {
+                callback.error(errorCode, message);
+            }
+
+            @Override
+            public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
+                callback.fileServicesUpdated(services);
+            }
+
+            @Override
+            public void middlewareReady() throws RemoteException {
+                callback.middlewareReady();
+            }
+        });
+    }
+
+    /**
      * Registers serviceClasses of interest with the appName/subId key.
      * Starts async fetching data on streaming services of matching classes to be reported
      * later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
@@ -107,19 +134,36 @@
      * @param downloadRequest An object describing the set of files to be downloaded.
      * @param listener A listener through which the middleware can provide progress updates to
      *                 the app while both are still running.
-     * @return TODO: enumerate possible return values
+     * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
+     *         or {@link MbmsException#SUCCESS}
+     */
+    public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * @hide
      */
     @Override
-    public int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
+    public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
             throws RemoteException {
-        return 0;
+        return download(downloadRequest, new DownloadProgressListener() {
+            @Override
+            public void progress(DownloadRequest request, FileInfo fileInfo, int
+                    currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
+                    fullDecodedSize) throws RemoteException {
+                listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
+                        currentDecodedSize, fullDecodedSize);
+            }
+        });
     }
 
 
     /**
      * Returns a list of pending {@link DownloadRequest}s that originated from the calling
      * application, identified by its uid. A pending request is one that was issued via
-     * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+     * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * The middleware must return a non-null result synchronously or throw an exception
      * inheriting from {@link RuntimeException}.
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 585d5b9..8f2786f 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -17,23 +17,29 @@
 package android.telephony.mbms.vendor;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
 import android.telephony.mbms.IStreamingServiceCallback;
 import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
 
 import java.util.List;
 
 /**
  * @hide
- * TODO: future systemapi
  */
+//@SystemApi
 public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
     /**
      * Initialize streaming service for this app and subId, registering the listener.
      *
-     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
      * will be intercepted and passed to the app as
      * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
      *
@@ -41,16 +47,61 @@
      * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
      * {@link IMbmsStreamingManagerCallback#error(int, String)}.
      *
-     * @param listener The callback to use to communicate with the app.
+     * @param callback The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
      */
-    @Override
-    public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId)
+    public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId)
             throws RemoteException {
         return 0;
     }
 
     /**
+     * Actual AIDL implementation that hides the callback AIDL from the middleware.
+     * @hide
+     */
+    @Override
+    public final int initialize(final IMbmsStreamingManagerCallback callback,
+            final int subscriptionId) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        callback.asBinder().linkToDeath(new DeathRecipient() {
+            @Override
+            public void binderDied() {
+                onAppCallbackDied(uid, subscriptionId);
+            }
+        }, 0);
+
+        return initialize(new MbmsStreamingManagerCallback() {
+            @Override
+            public void onError(int errorCode, String message) {
+                try {
+                    callback.error(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+                try {
+                    callback.streamingServicesUpdated(services);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onMiddlewareReady() {
+                try {
+                    callback.middlewareReady();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+        }, subscriptionId);
+    }
+
+
+    /**
      * Registers serviceClasses of interest with the appName/subId key.
      * Starts async fetching data on streaming services of matching classes to be reported
      * later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
@@ -82,13 +133,77 @@
      *
      * @param subscriptionId The subscription id to use.
      * @param serviceId The ID of the streaming service that the app has requested.
-     * @param listener The listener object on which the app wishes to receive updates.
+     * @param callback The callback object on which the app wishes to receive updates.
      * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
      */
+    public int startStreaming(int subscriptionId, String serviceId,
+            StreamingServiceCallback callback) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation of startStreaming that hides the callback AIDL from the
+     * middleware.
+     * @hide
+     */
     @Override
     public int startStreaming(int subscriptionId, String serviceId,
-            IStreamingServiceCallback listener) throws RemoteException {
-        return 0;
+            IStreamingServiceCallback callback) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        callback.asBinder().linkToDeath(new DeathRecipient() {
+            @Override
+            public void binderDied() {
+                onAppCallbackDied(uid, subscriptionId);
+            }
+        }, 0);
+
+        return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+            @Override
+            public void onError(int errorCode, String message) {
+                try {
+                    callback.error(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamStateUpdated(@StreamingService.StreamingState int state,
+                    @StreamingService.StreamingStateChangeReason int reason) {
+                try {
+                    callback.streamStateUpdated(state, reason);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onMediaDescriptionUpdated() {
+                try {
+                    callback.mediaDescriptionUpdated();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+                try {
+                    callback.broadcastSignalStrengthUpdated(signalStrength);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamMethodUpdated(int methodType) {
+                try {
+                    callback.streamMethodUpdated(methodType);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+        });
     }
 
     /**
@@ -153,4 +268,12 @@
     @Override
     public void dispose(int subscriptionId) throws RemoteException {
     }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorIntents.java b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java
new file mode 100644
index 0000000..367c995
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mbms.vendor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.MbmsDownloadReceiver;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @hide
+ * TODO: future systemapi
+ */
+public class VendorIntents {
+
+    /**
+     * The MBMS middleware should send this when a download of single file has completed or
+     * failed. Mandatory extras are
+     * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT}
+     * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO}
+     * {@link #EXTRA_REQUEST}
+     * {@link #EXTRA_TEMP_LIST}
+     * {@link #EXTRA_FINAL_URI}
+     */
+    public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
+            "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+
+    /**
+     * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
+     * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
+     * extras are
+     * {@link #EXTRA_REQUEST}
+     *
+     * Optional extras are
+     * {@link #EXTRA_FD_COUNT} (0 if not present)
+     * {@link #EXTRA_PAUSED_LIST} (empty if not present)
+     */
+    public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
+            "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+
+    /**
+     * The MBMS middleware should send this when it wishes to clean up temp  files in the app's
+     * filesystem. Mandatory extras are:
+     * {@link #EXTRA_TEMP_FILES_IN_USE}
+     */
+    public static final String ACTION_CLEANUP =
+            "android.telephony.mbms.action.CLEANUP";
+
+    /**
+     * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
+     * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
+     * files will be deleted upon receipt of the intent.
+     * May be null.
+     */
+    public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+
+    /**
+     * Extra containing an integer indicating the number of temp files requested.
+     */
+    public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+
+    /**
+     * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
+     * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
+     * should have scheme {@code file://}.
+     */
+    public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+
+    /**
+     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
+     * to be used for new file downloads.
+     */
+    public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+
+    /**
+     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
+     * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
+     * access to previously paused downloads.
+     */
+    public static final String EXTRA_PAUSED_URI_LIST =
+            "android.telephony.mbms.extra.PAUSED_URI_LIST";
+
+    /**
+     * Extra containing a string that points to the middleware's knowledge of where the temp file
+     * root for the app is. The path should be a canonical path as returned by
+     * {@link File#getCanonicalPath()}
+     */
+    public static final String EXTRA_TEMP_FILE_ROOT =
+            "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+    /**
+     * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
+     * still using.
+     */
+    public static final String EXTRA_TEMP_FILES_IN_USE =
+            "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+
+    /**
+     * Extra containing the {@link DownloadRequest} for which the download result or file
+     * descriptor request is for. Must not be null.
+     */
+    public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
+
+    /**
+     * Extra containing a single {@link Uri} indicating the path to the temp file in which the
+     * decoded downloaded file resides. Must not be null.
+     */
+    public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+
+    /**
+     * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
+     * file-descriptor requests and cleanup requests to specify which service they want to
+     * request temp files or clean up temp files for, respectively.
+     */
+    public static final String EXTRA_SERVICE_INFO =
+            "android.telephony.mbms.extra.SERVICE_INFO";
+
+    /**
+     * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+     * the various intents from the middleware should be targeted towards.
+     * @param uid The uid of the frontend app.
+     * @return The component name of the receiver that the middleware should send its intents to,
+     * or null if the app didn't declare it in the manifest.
+     */
+    public static ComponentName getAppReceiverFromUid(Context context, int uid) {
+        String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+        if (packageNames == null) {
+            return null;
+        }
+
+        for (String packageName : packageNames) {
+            ComponentName candidate = new ComponentName(packageName,
+                    MbmsDownloadReceiver.class.getCanonicalName());
+            Intent queryIntent = new Intent();
+            queryIntent.setComponent(candidate);
+            List<ResolveInfo> receivers =
+                    context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+            if (receivers != null && receivers.size() > 0) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+}