Merge "Unhide EMBMS download API" into oc-mr1-dev
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index d76fca5..f066539 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -27,11 +27,15 @@
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.net.Uri;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.InternalDownloadManagerCallback;
+import android.telephony.mbms.InternalDownloadStateCallback;
 import android.telephony.mbms.MbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsDownloadReceiver;
 import android.telephony.mbms.MbmsException;
@@ -62,7 +66,7 @@
      * interface.
      * @hide
      */
-    @SystemApi
+    //@SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
             "android.telephony.action.EmbmsDownload";
@@ -76,7 +80,8 @@
      *
      * This extra may also be used by the middleware when it is sending intents to the app.
      */
-    public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
+    public static final String EXTRA_MBMS_DOWNLOAD_RESULT =
+            "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
 
     /**
      * {@link FileInfo} extra that Android will attach to the intent supplied via
@@ -85,21 +90,21 @@
      *
      * This extra may also be used by the middleware when it is sending intents to the app.
      */
-    public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
+    public static final String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
 
     /**
      * {@link Uri} extra that Android will attach to the intent supplied via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
      * Indicates the location of the successfully
-     * downloaded file. Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
-     * {@link #RESULT_SUCCESSFUL}.
+     * downloaded file. Will always be set to a non-null value if
+     * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
      */
-    public static final String EXTRA_COMPLETED_FILE_URI =
-            "android.telephony.mbms.extra.COMPLETED_FILE_URI";
+    public static final String EXTRA_MBMS_COMPLETED_FILE_URI =
+            "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
 
     /**
      * The default directory name for all MBMS temp files. If you call
-     * {@link #download(DownloadRequest, DownloadStateCallback)} without first calling
+     * {@link #download(DownloadRequest, DownloadStateCallback, Handler)} without first calling
      * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
      * path returned by {@link Context#getFilesDir()}.
      */
@@ -173,24 +178,47 @@
     };
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
-    private final MbmsDownloadManagerCallback mCallback;
+    private final InternalDownloadManagerCallback mInternalCallback;
 
-    private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback, int subId) {
+    private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback,
+            int subscriptionId, Handler handler) {
         mContext = context;
-        mCallback = callback;
-        mSubscriptionId = subId;
+        mSubscriptionId = subscriptionId;
+        if (handler == null) {
+            handler = new Handler(Looper.myLooper());
+        }
+        mInternalCallback = new InternalDownloadManagerCallback(callback, handler);
+    }
+
+    /**
+     * Create a new MbmsDownloadManager using the system default data subscription ID and default
+     * {@link Handler}
+     * See {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
+     */
+    public static MbmsDownloadManager create(Context context,
+            MbmsDownloadManagerCallback callback)
+            throws MbmsException {
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
     }
 
     /**
      * Create a new MbmsDownloadManager using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadManagerCallback, int)}
-     *
-     * @hide
+     * See {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
      */
     public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback listener)
+            MbmsDownloadManagerCallback callback, Handler handler)
             throws MbmsException {
-        return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+    }
+
+    /**
+     * Create a new MbmsDownloadManager using the default {@link Handler}
+     * See {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
+     */
+    public static MbmsDownloadManager create(Context context,
+            MbmsDownloadManagerCallback callback, int subscriptionId)
+            throws MbmsException {
+        return create(context, callback, subscriptionId, null);
     }
 
     /**
@@ -198,7 +226,7 @@
      *
      * 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 MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+     * {@link MbmsDownloadManagerCallback#onMiddlewareReady()} 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 an {@link IllegalStateException}.
@@ -208,7 +236,7 @@
      * (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)}.
+     * {@link MbmsDownloadManagerCallback#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
@@ -217,15 +245,15 @@
      * @param context The instance of {@link Context} to use
      * @param listener A callback to get asynchronous error messages and file service updates.
      * @param subscriptionId The data subscription ID to use
-     * @hide
      */
     public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback listener, int subscriptionId)
+            MbmsDownloadManagerCallback listener, int subscriptionId, Handler handler)
             throws MbmsException {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
         }
-        MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
+        MbmsDownloadManager mdm =
+                new MbmsDownloadManager(context, listener, subscriptionId, handler);
         try {
             mdm.bindAndInitialize();
         } catch (MbmsException e) {
@@ -244,7 +272,7 @@
                                 IMbmsDownloadService.Stub.asInterface(service);
                         int result;
                         try {
-                            result = downloadService.initialize(mSubscriptionId, mCallback);
+                            result = downloadService.initialize(mSubscriptionId, mInternalCallback);
                         } catch (RemoteException e) {
                             Log.e(LOG_TAG, "Service died before initialization");
                             sIsInitialized.set(false);
@@ -285,7 +313,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 MbmsDownloadManagerCallback#fileServicesUpdated(List)}
+     * {@link MbmsDownloadManagerCallback#onFileServicesUpdated(List)}
      *
      * The serviceClasses argument lets the app filter on types of programming and is opaque data
      * negotiated beforehand between the app and the carrier.
@@ -294,12 +322,12 @@
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
      * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
      *
-     * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
+     * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#onError(int, String)}
      * callback can include any of the errors except:
      * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
      *
      * @param classList A list of service classes which the app wishes to receive
-     *                  {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
+     *                  {@link MbmsDownloadManagerCallback#onFileServicesUpdated(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).
@@ -329,7 +357,7 @@
      * 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, DownloadStateCallback)}, the framework
+     * {@link #download(DownloadRequest, DownloadStateCallback, Handler)}, the framework
      * will default to a directory formed by the concatenation of the app's files directory and
      * {@link MbmsDownloadManager#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
      *
@@ -380,9 +408,9 @@
     /**
      * 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, DownloadStateCallback)} 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.
+     * {@link #download(DownloadRequest, DownloadStateCallback, Handler)} 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 {@code null}.
      *
      * @return A {@link File} pointing to the configured temp file directory, or null if not yet
      *         configured.
@@ -410,11 +438,15 @@
      * Asynchronous errors through the listener include any of the errors
      *
      * @param request The request that specifies what should be downloaded
-     * @param progressListener Optional listener that will be provided progress updates
+     * @param stateCallback Optional listener that will be provided progress updates
      *                         if the app is running. If {@code null}, no callbacks will be
      *                         provided.
+     * @param handler A handler that calls to {@code stateCallback} should be called on. If
+     *                null, defaults to the handler provided via
+     *                {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
      */
-    public void download(DownloadRequest request, @Nullable DownloadStateCallback progressListener)
+    public void download(DownloadRequest request, @Nullable DownloadStateCallback stateCallback,
+            Handler handler)
             throws MbmsException {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
@@ -430,11 +462,16 @@
             tempRootDirectory.mkdirs();
             setTempFileRootDirectory(tempRootDirectory);
         }
+        InternalDownloadStateCallback internalCallback = null;
+        if (stateCallback != null) {
+            internalCallback = new InternalDownloadStateCallback(stateCallback,
+                    handler == null ? mInternalCallback.getHandler() : handler);
+        }
 
         checkValidDownloadDestination(request);
         writeDownloadRequestToken(request);
         try {
-            downloadService.download(request, progressListener);
+            downloadService.download(request, internalCallback);
         } catch (RemoteException e) {
             mService.set(null);
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
@@ -444,7 +481,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, DownloadStateCallback)} but not cancelled through
+     * {@link #download(DownloadRequest, DownloadStateCallback, Handler)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * @return A list, possibly empty, of {@link DownloadRequest}s
      */
@@ -636,7 +673,7 @@
 
     private void sendErrorToApp(int errorCode, String message) {
         try {
-            mCallback.error(errorCode, message);
+            mInternalCallback.error(errorCode, message);
         } catch (RemoteException e) {
             // Ignore, should not happen locally.
         }
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f75970b..8c3f71d 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -43,21 +43,15 @@
  * to the app when the middleware updates the status of the download.
  * @hide
  */
-public class DownloadRequest implements Parcelable {
+public final class DownloadRequest implements Parcelable {
     // Version code used to keep token calculation consistent.
     private static final int CURRENT_VERSION = 1;
     private static final String LOG_TAG = "MbmsDownloadRequest";
 
-    /**
-     * Maximum permissible length for the app's download-completion intent, when serialized via
-     * {@link Intent#toUri(int)}.
-     */
+    /** @hide */
     public static final int MAX_APP_INTENT_SIZE = 50000;
 
-    /**
-     * Maximum permissible length for the app's destination path, when serialized via
-     * {@link Uri#toString()}.
-     */
+    /** @hide */
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
@@ -104,7 +98,6 @@
         /**
          * Sets the source URI for the download request to be built.
          * @param source
-         * @return
          */
         public Builder setSource(Uri source) {
             this.source = source;
@@ -116,7 +109,6 @@
          * 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) {
@@ -130,7 +122,6 @@
         /**
          * Set the subscription ID on which the file(s) should be downloaded.
          * @param subscriptionId
-         * @return
          */
         public Builder setSubscriptionId(int subscriptionId) {
             this.subscriptionId = subscriptionId;
@@ -144,7 +135,6 @@
          *
          * The middleware should not use this method.
          * @param intent
-         * @return
          */
         public Builder setAppIntent(Intent intent) {
             this.appIntent = intent.toUri(0);
@@ -161,7 +151,6 @@
          * 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
          * @hide
          */
         //@SystemApi
@@ -324,6 +313,22 @@
     };
 
     /**
+     * Maximum permissible length for the app's destination path, when serialized via
+     * {@link Uri#toString()}.
+     */
+    public static int getMaxAppIntentSize() {
+        return MAX_APP_INTENT_SIZE;
+    }
+
+    /**
+     * Maximum permissible length for the app's download-completion intent, when serialized via
+     * {@link Intent#toUri(int)}.
+     */
+    public static int getMaxDestinationUriSize() {
+        return MAX_DESTINATION_URI_SIZE;
+    }
+
+    /**
      * @hide
      */
     public boolean isMultipartDownload() {
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index a144f3e..9530641 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,19 +16,20 @@
 
 package android.telephony.mbms;
 
-import android.os.RemoteException;
+import android.os.Handler;
 import android.telephony.MbmsDownloadManager;
 
 /**
  * A optional listener class used by download clients to track progress. Apps should extend this
  * class and pass an instance into
- * {@link android.telephony.MbmsDownloadManager#download(DownloadRequest, DownloadStateCallback)}
+ * {@link android.telephony.MbmsDownloadManager#download(
+ * DownloadRequest, DownloadStateCallback, Handler)}
  *
  * This is optionally specified when requesting a download and will only be called while the app
  * is running.
  * @hide
  */
-public class DownloadStateCallback extends IDownloadStateCallback.Stub {
+public class DownloadStateCallback {
 
     /**
      * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
@@ -44,10 +45,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,
+    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
             int currentDownloadSize, int fullDownloadSize,
-            int currentDecodedSize, int fullDecodedSize) throws RemoteException {
+            int currentDecodedSize, int fullDecodedSize) {
     }
 
     /**
@@ -59,8 +59,7 @@
      *   may not have been able to get a list of them in advance.
      * @param state The current state of the download.
      */
-    @Override
-    public void state(DownloadRequest request, FileInfo fileInfo,
+    public void onStateChanged(DownloadRequest request, FileInfo fileInfo,
             @MbmsDownloadManager.DownloadStatus int state) {
     }
 }
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 7137f24..bfa99e3 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -25,7 +25,7 @@
  * Describes a single file that is available over MBMS.
  * @hide
  */
-public class FileInfo implements Parcelable {
+public final class FileInfo implements Parcelable {
 
     private final Uri uri;
 
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 07c58a1..b2e80ab 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -31,11 +31,11 @@
  * cell-broadcast.
  * @hide
  */
-public class FileServiceInfo extends ServiceInfo implements Parcelable {
+public final class FileServiceInfo extends ServiceInfo implements Parcelable {
     private final List<FileInfo> files;
 
     /** @hide */
-    @SystemApi
+    //@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/InternalDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadManagerCallback.java
new file mode 100644
index 0000000..fe2d719
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadManagerCallback.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/** @hide */
+public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
+
+    private final Handler mHandler;
+    private final MbmsDownloadManagerCallback mAppCallback;
+
+    public InternalDownloadManagerCallback(MbmsDownloadManagerCallback appCallback,
+            Handler handler) {
+        mAppCallback = appCallback;
+        mHandler = handler;
+    }
+
+    @Override
+    public void error(final int errorCode, final String message) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onError(errorCode, message);
+            }
+        });
+    }
+
+    @Override
+    public void fileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onFileServicesUpdated(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/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
new file mode 100644
index 0000000..32be16b
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
+    private final Handler mHandler;
+    private final DownloadStateCallback mAppCallback;
+
+    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+        mAppCallback = appCallback;
+        mHandler = handler;
+    }
+
+    @Override
+    public void progress(final DownloadRequest request, final FileInfo fileInfo,
+            final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
+            final int fullDecodedSize) throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+            }
+        });
+    }
+
+    @Override
+    public void state(final DownloadRequest request, final FileInfo fileInfo, final int state)
+            throws RemoteException {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onStateChanged(request, fileInfo, state);
+            }
+        });
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index 90509e1..1459844 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -16,7 +16,6 @@
 
 package android.telephony.mbms;
 
-import android.os.RemoteException;
 import android.telephony.MbmsDownloadManager;
 
 import java.util.List;
@@ -26,17 +25,15 @@
  * cell-broadcast.
  * @hide
  */
-public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
+public class MbmsDownloadManagerCallback {
 
     /**
      * Indicates that the middleware has encountered an asynchronous error.
      * @param errorCode Any error code listed in {@link MbmsException}
      * @param message A message, intended for debugging purposes, describing the error in further
      *                detail.
-     * @throws RemoteException
      */
-    @Override
-    public void error(int errorCode, String message) throws RemoteException {
+    public void onError(int errorCode, String message) {
         // default implementation empty
     }
 
@@ -51,8 +48,7 @@
      *
      * @param services The most recently updated list of available file services.
      */
-    @Override
-    public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
+    public void onFileServicesUpdated(List<FileServiceInfo> services) {
         // default implementation empty
     }
 
@@ -64,8 +60,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() throws RemoteException {
+    public void onMiddlewareReady() {
         // default implementation empty
     }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 8745d8e..7ee337a 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -149,7 +149,7 @@
 
     private boolean verifyIntentContents(Context context, Intent intent) {
         if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
+            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT)) {
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
@@ -161,7 +161,7 @@
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
+            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO)) {
                 Log.w(LOG_TAG, "Download result did not include the associated file info. " +
                         "Ignoring.");
                 return false;
@@ -214,9 +214,9 @@
         DownloadRequest request = intent.getParcelableExtra(VendorUtils.EXTRA_REQUEST);
         Intent intentForApp = request.getIntentForApp();
 
-        int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
+        int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT,
                 MbmsDownloadManager.RESULT_CANCELLED);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_RESULT, result);
+        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT, result);
 
         if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) {
             Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
@@ -233,7 +233,7 @@
         }
 
         FileInfo completedFileInfo =
-                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
+                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO);
         String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
 
         Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
@@ -242,8 +242,8 @@
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);
+        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_COMPLETED_FILE_URI, finalFileLocation);
+        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
         setResultCode(RESULT_OK);
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 601bfc9..144a8a0 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -117,7 +117,6 @@
 
     /**
      * Indicates the errors that are applicable only to the file-download use-case
-     * @hide
      */
     public static class DownloadErrors {
         private DownloadErrors() { }
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index bb3823a..33bf785 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -29,7 +29,7 @@
  * @hide
  */
 //@SystemApi
-public class UriPathPair implements Parcelable {
+public final class UriPathPair implements Parcelable {
     private final Uri mFilePathUri;
     private final Uri mContentUri;
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 0baa375..84ff533 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Intent;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.DownloadRequest;
@@ -62,22 +63,42 @@
      * @hide
      */
     @Override
-    public final int initialize(int subscriptionId,
+    public final int initialize(final int subscriptionId,
             final IMbmsDownloadManagerCallback callback) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        callback.asBinder().linkToDeath(new DeathRecipient() {
+            @Override
+            public void binderDied() {
+                onAppCallbackDied(uid, subscriptionId);
+            }
+        }, 0);
+
         return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
             @Override
-            public void error(int errorCode, String message) throws RemoteException {
-                callback.error(errorCode, message);
+            public void onError(int errorCode, String message) {
+                try {
+                    callback.error(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
 
             @Override
-            public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
-                callback.fileServicesUpdated(services);
+            public void onFileServicesUpdated(List<FileServiceInfo> services) {
+                try {
+                    callback.fileServicesUpdated(services);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
 
             @Override
-            public void middlewareReady() throws RemoteException {
-                callback.middlewareReady();
+            public void onMiddlewareReady() {
+                try {
+                    callback.middlewareReady();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
         });
     }
@@ -150,13 +171,25 @@
     @Override
     public final int download(DownloadRequest downloadRequest, IDownloadStateCallback callback)
             throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        callback.asBinder().linkToDeath(new DeathRecipient() {
+            @Override
+            public void binderDied() {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+            }
+        }, 0);
+
         return download(downloadRequest, new DownloadStateCallback() {
             @Override
-            public void progress(DownloadRequest request, FileInfo fileInfo, int
+            public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
                     currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
-                    fullDecodedSize) throws RemoteException {
-                callback.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
-                        currentDecodedSize, fullDecodedSize);
+                    fullDecodedSize) {
+                try {
+                    callback.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
+                            currentDecodedSize, fullDecodedSize);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                }
             }
         });
     }
@@ -246,4 +279,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/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index a01e57d..e667237 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -39,8 +39,8 @@
     /**
      * 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 android.telephony.MbmsDownloadManager#EXTRA_MBMS_DOWNLOAD_RESULT}
+     * {@link android.telephony.MbmsDownloadManager#EXTRA_MBMS_FILE_INFO}
      * {@link #EXTRA_REQUEST}
      * {@link #EXTRA_TEMP_LIST}
      * {@link #EXTRA_FINAL_URI}