Merge "Apply suggested edits to the file-download API" am: f6c9baa0cd am: 3934c475ce
am: febecb8218

Change-Id: Ibf5acac5b44c772b629472591124151c479c8f6c
diff --git a/Android.mk b/Android.mk
index 32b86a2..591ecf3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -502,7 +502,7 @@
 	telecomm/java/com/android/internal/telecom/IInCallService.aidl \
 	telecomm/java/com/android/internal/telecom/ITelecomService.aidl \
 	telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl \
-        telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl \
+	telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl \
 	telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl \
 	telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \
         telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 2720df0..5b15038 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -39910,20 +39910,20 @@
     field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
   }
 
-  public class MbmsDownloadManager {
-    method public void cancelDownload(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public void dispose();
-    method public void download(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.telephony.mbms.MbmsException;
-    method public void getFileServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
+  public class MbmsDownloadSession implements java.lang.AutoCloseable {
+    method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+    method public void close();
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+    method public void download(android.telephony.mbms.DownloadRequest);
+    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public java.io.File getTempFileRootDirectory();
-    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads() throws android.telephony.mbms.MbmsException;
-    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public void setTempFileRootDirectory(java.io.File) throws android.telephony.mbms.MbmsException;
+    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+    method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+    method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+    method public void setTempFileRootDirectory(java.io.File);
+    method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
     field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
     field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
@@ -40605,7 +40605,7 @@
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
-    method public void onStateChanged(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -40623,18 +40623,18 @@
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
   }
 
-  public class MbmsDownloadManagerCallback {
-    ctor public MbmsDownloadManagerCallback();
-    method public void onError(int, java.lang.String);
-    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
-    method public void onMiddlewareReady();
-  }
-
   public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
     ctor public MbmsDownloadReceiver();
     method public void onReceive(android.content.Context, android.content.Intent);
   }
 
+  public class MbmsDownloadSessionCallback {
+    ctor public MbmsDownloadSessionCallback();
+    method public void onError(int, java.lang.String);
+    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+    method public void onMiddlewareReady();
+  }
+
   public class MbmsException extends java.lang.Exception {
     method public int getErrorCode();
     field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
@@ -40679,7 +40679,7 @@
 
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
-    method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+    method public java.lang.CharSequence getNameForLocale(java.util.Locale);
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5c9f7a9..37cb987 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -43349,20 +43349,20 @@
     field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
   }
 
-  public class MbmsDownloadManager {
-    method public void cancelDownload(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public void dispose();
-    method public void download(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.telephony.mbms.MbmsException;
-    method public void getFileServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
+  public class MbmsDownloadSession implements java.lang.AutoCloseable {
+    method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+    method public void close();
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+    method public void download(android.telephony.mbms.DownloadRequest);
+    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public java.io.File getTempFileRootDirectory();
-    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads() throws android.telephony.mbms.MbmsException;
-    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public void setTempFileRootDirectory(java.io.File) throws android.telephony.mbms.MbmsException;
+    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+    method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+    method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+    method public void setTempFileRootDirectory(java.io.File);
+    method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
     field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
     field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
@@ -44166,7 +44166,7 @@
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
-    method public void onStateChanged(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -44186,13 +44186,6 @@
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
   }
 
-  public class MbmsDownloadManagerCallback {
-    ctor public MbmsDownloadManagerCallback();
-    method public void onError(int, java.lang.String);
-    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
-    method public void onMiddlewareReady();
-  }
-
   public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
     ctor public MbmsDownloadReceiver();
     method public void onReceive(android.content.Context, android.content.Intent);
@@ -44204,6 +44197,13 @@
     field public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5; // 0x5
   }
 
+  public class MbmsDownloadSessionCallback {
+    ctor public MbmsDownloadSessionCallback();
+    method public void onError(int, java.lang.String);
+    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+    method public void onMiddlewareReady();
+  }
+
   public class MbmsException extends java.lang.Exception {
     method public int getErrorCode();
     field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
@@ -44248,7 +44248,7 @@
 
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
-    method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+    method public java.lang.CharSequence getNameForLocale(java.util.Locale);
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
@@ -44306,14 +44306,16 @@
     ctor public MbmsDownloadServiceBase();
     method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public void dispose(int) throws android.os.RemoteException;
-    method public int download(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+    method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
-    method public int getFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
-    method public int initialize(int, android.telephony.mbms.MbmsDownloadManagerCallback) throws android.os.RemoteException;
+    method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
+    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+    method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
     method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
+    method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/api/test-current.txt b/api/test-current.txt
index 37555af..7a46776 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -40181,20 +40181,20 @@
     field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
   }
 
-  public class MbmsDownloadManager {
-    method public void cancelDownload(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int) throws android.telephony.mbms.MbmsException;
-    method public static android.telephony.MbmsDownloadManager create(android.content.Context, android.telephony.mbms.MbmsDownloadManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public void dispose();
-    method public void download(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
-    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.telephony.mbms.MbmsException;
-    method public void getFileServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
+  public class MbmsDownloadSession implements java.lang.AutoCloseable {
+    method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+    method public void close();
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+    method public void download(android.telephony.mbms.DownloadRequest);
+    method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public java.io.File getTempFileRootDirectory();
-    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads() throws android.telephony.mbms.MbmsException;
-    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.telephony.mbms.MbmsException;
-    method public void setTempFileRootDirectory(java.io.File) throws android.telephony.mbms.MbmsException;
+    method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+    method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+    method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+    method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+    method public void setTempFileRootDirectory(java.io.File);
+    method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
     field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
     field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
@@ -40876,7 +40876,7 @@
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
-    method public void onStateChanged(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -40894,18 +40894,18 @@
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
   }
 
-  public class MbmsDownloadManagerCallback {
-    ctor public MbmsDownloadManagerCallback();
-    method public void onError(int, java.lang.String);
-    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
-    method public void onMiddlewareReady();
-  }
-
   public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
     ctor public MbmsDownloadReceiver();
     method public void onReceive(android.content.Context, android.content.Intent);
   }
 
+  public class MbmsDownloadSessionCallback {
+    ctor public MbmsDownloadSessionCallback();
+    method public void onError(int, java.lang.String);
+    method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+    method public void onMiddlewareReady();
+  }
+
   public class MbmsException extends java.lang.Exception {
     method public int getErrorCode();
     field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
@@ -40950,7 +40950,7 @@
 
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
-    method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+    method public java.lang.CharSequence getNameForLocale(java.util.Locale);
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadSession.java
similarity index 63%
rename from telephony/java/android/telephony/MbmsDownloadManager.java
rename to telephony/java/android/telephony/MbmsDownloadSession.java
index 302bc6a..f991c5e 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -34,9 +34,9 @@
 import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.InternalDownloadManagerCallback;
+import android.telephony.mbms.InternalDownloadSessionCallback;
 import android.telephony.mbms.InternalDownloadStateCallback;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsDownloadReceiver;
 import android.telephony.mbms.MbmsException;
 import android.telephony.mbms.MbmsTempFileProvider;
@@ -48,7 +48,10 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -57,8 +60,8 @@
 /**
  * This class provides functionality for file download over MBMS.
  */
-public class MbmsDownloadManager {
-    private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
+public class MbmsDownloadSession implements AutoCloseable {
+    private static final String LOG_TAG = MbmsDownloadSession.class.getSimpleName();
 
     /**
      * Service action which must be handled by the middleware implementing the MBMS file download
@@ -103,7 +106,7 @@
 
     /**
      * The default directory name for all MBMS temp files. If you call
-     * {@link #download(DownloadRequest, DownloadStateCallback, Handler)} without first calling
+     * {@link #download(DownloadRequest)} without first calling
      * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
      * path returned by {@link Context#getFilesDir()}.
      */
@@ -177,93 +180,83 @@
     };
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
-    private final InternalDownloadManagerCallback mInternalCallback;
+    private final InternalDownloadSessionCallback mInternalCallback;
+    private final Map<DownloadStateCallback, InternalDownloadStateCallback>
+            mInternalDownloadCallbacks = new HashMap<>();
 
-    private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback,
+    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
             int subscriptionId, Handler handler) {
         mContext = context;
         mSubscriptionId = subscriptionId;
         if (handler == null) {
-            handler = new Handler(Looper.myLooper());
+            handler = new Handler(Looper.getMainLooper());
         }
-        mInternalCallback = new InternalDownloadManagerCallback(callback, handler);
+        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
     }
 
     /**
-     * Create a new MbmsDownloadManager using the system default data subscription ID and default
-     * {@link Handler}
-     * See {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
+     * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
+     * See {@link #create(Context, MbmsDownloadSessionCallback, 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, Handler)}
-     */
-    public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback callback, Handler handler)
-            throws MbmsException {
+    public static MbmsDownloadSession create(@NonNull Context context,
+            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
         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);
-    }
-
-    /**
      * Create a new MbmsDownloadManager using the given subscription ID.
      *
      * 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#onMiddlewareReady()} is called on the provided callback.
-     * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
+     * {@link MbmsDownloadSession} that is returned will not be ready for use until
+     * {@link MbmsDownloadSessionCallback#onMiddlewareReady()} is called on the provided callback.
+     * If you attempt to use the instance before it is ready, an {@link IllegalStateException}
+     * will be thrown or an error will be delivered through
+     * {@link MbmsDownloadSessionCallback#onError(int, String)}.
      *
-     * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+     * This also may throw an {@link IllegalArgumentException}.
      *
-     * 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
+     * You may only have one instance of {@link MbmsDownloadSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsDownloadSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
      * running under the same UID, an error will be indicated via
-     * {@link MbmsDownloadManagerCallback#onError(int, String)}.
+     * {@link MbmsDownloadSessionCallback#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 MbmsDownloadManager} that you received before calling this method again.
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsDownloadSession} 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.
+     * @param callback A callback to get asynchronous error messages and file service updates.
      * @param subscriptionId The data subscription ID to use
+     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
+     * setup.
      */
-    public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback listener, int subscriptionId, Handler handler)
-            throws MbmsException {
+    public static @Nullable MbmsDownloadSession create(@NonNull Context context,
+            final @NonNull MbmsDownloadSessionCallback callback,
+            int subscriptionId, @NonNull Handler handler) {
         if (!sIsInitialized.compareAndSet(false, true)) {
-            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+            throw new IllegalStateException("Cannot have two active instances");
         }
-        MbmsDownloadManager mdm =
-                new MbmsDownloadManager(context, listener, subscriptionId, handler);
-        try {
-            mdm.bindAndInitialize();
-        } catch (MbmsException e) {
+        MbmsDownloadSession session =
+                new MbmsDownloadSession(context, callback, subscriptionId, handler);
+        final int result = session.bindAndInitialize();
+        if (result != MbmsException.SUCCESS) {
             sIsInitialized.set(false);
-            throw e;
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
         }
-        return mdm;
+        return session;
     }
 
-    private void bindAndInitialize() throws MbmsException {
-        MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+    private int bindAndInitialize() {
+        return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
                 new ServiceConnection() {
                     @Override
                     public void onServiceConnected(ComponentName name, IBinder service) {
@@ -312,39 +305,35 @@
      * 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#onFileServicesUpdated(List)}
+     * {@link MbmsDownloadSessionCallback#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.
+     * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+     * callback may include any of the errors that are not specific to the streaming use-case.
      *
-     * This may throw an {@link MbmsException} containing one of the following errors:
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
-     *
-     * 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}
+     * May throw an {@link IllegalStateException} or {@link IllegalArgumentException}.
      *
      * @param classList A list of service classes which the app wishes to receive
-     *                  {@link MbmsDownloadManagerCallback#onFileServicesUpdated(List)} callbacks
+     *                  {@link MbmsDownloadSessionCallback#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).
+     *                  Values in this list should be negotiated with the wireless carrier prior
+     *                  to using this API.
      */
-    public void getFileServices(List<String> classList) throws MbmsException {
+    public void requestUpdateFileServices(@NonNull List<String> classList) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
         try {
-            int returnCode = downloadService.getFileServices(mSubscriptionId, classList);
+            int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
             if (returnCode != MbmsException.SUCCESS) {
-                throw new MbmsException(returnCode);
+                sendErrorToApp(returnCode, null);
             }
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
@@ -356,31 +345,32 @@
      * 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, Handler)}, the framework
+     * {@link #download(DownloadRequest)}, 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}.
+     * {@link MbmsDownloadSession#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
+     * you will receive an asynchronous error with code
      * {@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.
+     * {@link IllegalArgumentException} will be thrown. In addition, as an additional sanity
+     * check, an {@link IllegalArgumentException} will be thrown if you attempt to set the temp
+     * file root directory to one of your data roots (the value of {@link Context#getDataDir()},
+     * {@link Context#getFilesDir()}, or {@link Context#getCacheDir()}).
      * @param tempFileRootDirectory A directory to place temp files in.
      */
-    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
-            throws MbmsException {
+    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
-        if (!tempFileRootDirectory.exists()) {
-            throw new IllegalArgumentException("Provided directory does not exist");
-        }
-        if (!tempFileRootDirectory.isDirectory()) {
-            throw new IllegalArgumentException("Provided File is not a directory");
+        try {
+            validateTempFileRootSanity(tempFileRootDirectory);
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException checking directory sanity");
         }
         String filePath;
         try {
@@ -392,11 +382,12 @@
         try {
             int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
             if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
+                sendErrorToApp(result, null);
             }
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            return;
         }
 
         SharedPreferences prefs = mContext.getSharedPreferences(
@@ -404,10 +395,28 @@
         prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
     }
 
+    private void validateTempFileRootSanity(File tempFileRootDirectory) throws IOException {
+        if (!tempFileRootDirectory.exists()) {
+            throw new IllegalArgumentException("Provided directory does not exist");
+        }
+        if (!tempFileRootDirectory.isDirectory()) {
+            throw new IllegalArgumentException("Provided File is not a directory");
+        }
+        String canonicalTempFilePath = tempFileRootDirectory.getCanonicalPath();
+        if (mContext.getDataDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your data dir");
+        }
+        if (mContext.getCacheDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your cache dir");
+        }
+        if (mContext.getFilesDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your files dir");
+        }
+    }
     /**
      * 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, Handler)} was called without ever
+     * {@link #download(DownloadRequest)} 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}.
      *
@@ -425,31 +434,24 @@
     }
 
     /**
-     * Requests a download of a file that is available via multicast.
+     * Requests the download of a file or set of files that the carrier has indicated to be
+     * available.
      *
      * May throw an {@link IllegalArgumentException}
      *
      * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
      * this method will create a directory at the default location defined at
-     * {@link MbmsDownloadManager#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+     * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
      * file root directory.
      *
-     * Asynchronous errors through the listener include any of the errors
-     *
-     * @param request The request that specifies what should be downloaded
-     * @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)}
+     * Asynchronous errors through the callback may include any error not specific to the
+     * streaming use-case.
+     * @param request The request that specifies what should be downloaded.
      */
-    public void download(DownloadRequest request, @Nullable DownloadStateCallback stateCallback,
-            Handler handler)
-            throws MbmsException {
+    public void download(@NonNull DownloadRequest request) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
 
         // Check to see whether the app's set a temp root dir yet, and set it if not.
@@ -461,67 +463,152 @@
             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, internalCallback);
+            downloadService.download(request);
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
     /**
      * 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, Handler)} but not cancelled through
+     * {@link #download(DownloadRequest)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * @return A list, possibly empty, of {@link DownloadRequest}s
      */
-    public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException {
+    public @NonNull List<DownloadRequest> listPendingDownloads() {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
             return downloadService.listPendingDownloads(mSubscriptionId);
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Registers a callback for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadStateCallback} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param callback The callback that should be called when the middleware has information to
+     *                 share on the download.
+     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
+     */
+    public void registerStateCallback(@NonNull DownloadRequest request,
+            @NonNull DownloadStateCallback callback,
+            @NonNull Handler handler) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadStateCallback internalCallback =
+                new InternalDownloadStateCallback(callback, handler);
+
+        try {
+            int result = downloadService.registerStateCallback(request, internalCallback);
+            if (result != MbmsException.SUCCESS) {
+                if (result == MbmsException.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadCallbacks.put(callback, internalCallback);
+    }
+
+    /**
+     * Un-register a callback previously registered via
+     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param callback The callback provided during registration.
+     */
+    public void unregisterStateCallback(@NonNull DownloadRequest request,
+            @NonNull DownloadStateCallback callback) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadStateCallback internalCallback =
+                    mInternalDownloadCallbacks.get(callback);
+
+            try {
+                int result = downloadService.unregisterStateCallback(request, internalCallback);
+                if (result != MbmsException.SUCCESS) {
+                    if (result == MbmsException.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            }
+        } finally {
+            InternalDownloadStateCallback internalCallback =
+                    mInternalDownloadCallbacks.remove(callback);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
         }
     }
 
     /**
      * Attempts to cancel the specified {@link DownloadRequest}.
      *
-     * If the middleware is not aware of the specified download request, an MbmsException will be
-     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
      *
-     * If this method returns without throwing an exception, you may assume that cancellation
-     * was successful.
      * @param downloadRequest The download request that you wish to cancel.
      */
-    public void cancelDownload(DownloadRequest downloadRequest) throws MbmsException {
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
             int result = downloadService.cancelDownload(downloadRequest);
             if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
+                if (result == MbmsException.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
             }
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            return;
         }
         deleteDownloadRequestToken(downloadRequest);
     }
@@ -529,7 +616,7 @@
     /**
      * Gets information about the status of a file pending download.
      *
-     * If the middleware has not yet been properly initialized or if it has no records of the
+     * If there was a problem communicating with the middleware or if it has no records of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
      * {@link #STATUS_UNKNOWN} will be returned.
      *
@@ -538,18 +625,18 @@
      * @return The status of the download.
      */
     @DownloadStatus
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
-            throws MbmsException {
+    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
             return downloadService.getDownloadStatus(downloadRequest, fileInfo);
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
+            return STATUS_UNKNOWN;
         }
     }
 
@@ -565,30 +652,50 @@
      * when available.
      * This will not interrupt in-progress downloads.
      *
-     * If the middleware is not aware of the specified download request, an MbmsException will be
-     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     * This is distinct from cancelling and re-issuing the download request -- if you cancel and
+     * re-issue, the middleware will not clear its cache of download state information.
      *
-     * May throw a {@link MbmsException} with error code
+     * If the middleware is not aware of the specified download request, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
      * @param downloadRequest The request to re-download files for.
      */
-    public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException {
+    public void resetDownloadKnowledge(DownloadRequest downloadRequest) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+            throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
             int result = downloadService.resetDownloadKnowledge(downloadRequest);
             if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
+                if (result == MbmsException.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
             }
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
-    public void dispose() {
+    /**
+     * Terminates this instance.
+     *
+     * After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * obtain another instance of {@link MbmsDownloadSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
         try {
             IMbmsDownloadService downloadService = mService.get();
             if (downloadService == null) {
@@ -602,6 +709,7 @@
         } finally {
             mService.set(null);
             sIsInitialized.set(false);
+            mInternalCallback.stop();
         }
     }
 
@@ -672,7 +780,7 @@
 
     private void sendErrorToApp(int errorCode, String message) {
         try {
-            mInternalCallback.error(errorCode, message);
+            mInternalCallback.onError(errorCode, message);
         } catch (RemoteException e) {
             // Ignore, should not happen locally.
         }
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index 5550ac1..32dd532 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -115,7 +115,7 @@
      * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
      */
     public static @Nullable MbmsStreamingSession create(@NonNull Context context,
-            @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
+            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
             @NonNull Handler handler) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
@@ -123,7 +123,7 @@
         MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
                 subscriptionId, handler);
 
-        int result = session.bindAndInitialize();
+        final int result = session.bindAndInitialize();
         if (result != MbmsException.SUCCESS) {
             sIsInitialized.set(false);
             handler.post(new Runnable() {
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 0508a38..86920bd 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,14 +16,12 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
-import android.telephony.MbmsDownloadManager;
+import android.telephony.MbmsDownloadSession;
 
 /**
  * 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, Handler)}
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
  *
  * This is optionally specified when requesting a download and will only be called while the app
  * is running.
@@ -58,7 +56,7 @@
      *   may not have been able to get a list of them in advance.
      * @param state The current state of the download.
      */
-    public void onStateChanged(DownloadRequest request, FileInfo fileInfo,
-            @MbmsDownloadManager.DownloadStatus int state) {
+    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus int state) {
     }
 }
diff --git a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl b/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
index d62247b..cebc70d 100755
--- a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
@@ -29,8 +29,9 @@
      * Gives progress callbacks for a given DownloadRequest.  Includes a FileInfo
      * as the list of files may not have been known at request-time.
      */
-    void progress(in DownloadRequest request, in FileInfo fileInfo, int currentDownloadSize,
-            int fullDownloadSize, int currentDecodedSize, int fullDecodedSize);
+    void onProgressUpdated(in DownloadRequest request, in FileInfo fileInfo,
+            int currentDownloadSize, int fullDownloadSize,
+            int currentDecodedSize, int fullDecodedSize);
 
-    void state(in DownloadRequest request, in FileInfo fileInfo, int state);
+    void onStateUpdated(in DownloadRequest request, in FileInfo fileInfo, int state);
 }
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
similarity index 80%
rename from telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
rename to telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
index ac2f202..0d813a7 100755
--- a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
@@ -24,11 +24,11 @@
  * The interface the clients top-level file download listener will satisfy.
  * @hide
  */
-oneway interface IMbmsDownloadManagerCallback
+oneway interface IMbmsDownloadSessionCallback
 {
-    void error(int errorCode, String message);
+    void onError(int errorCode, String message);
 
-    void fileServicesUpdated(in List<FileServiceInfo> services);
+    void onFileServicesUpdated(in List<FileServiceInfo> services);
 
-    void middlewareReady();
+    void onMiddlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
similarity index 67%
rename from telephony/java/android/telephony/mbms/InternalDownloadManagerCallback.java
rename to telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index fe2d719..a7a5958 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -22,19 +22,24 @@
 import java.util.List;
 
 /** @hide */
-public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
+public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
     private final Handler mHandler;
-    private final MbmsDownloadManagerCallback mAppCallback;
+    private final MbmsDownloadSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
 
-    public InternalDownloadManagerCallback(MbmsDownloadManagerCallback appCallback,
+    public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
             Handler handler) {
         mAppCallback = appCallback;
         mHandler = handler;
     }
 
     @Override
-    public void error(final int errorCode, final String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -44,7 +49,11 @@
     }
 
     @Override
-    public void fileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -54,7 +63,11 @@
     }
 
     @Override
-    public void middlewareReady() throws RemoteException {
+    public void onMiddlewareReady() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -66,4 +79,8 @@
     public Handler getHandler() {
         return mHandler;
     }
+
+    public void stop() {
+        mIsStopped = true;
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
index 32be16b..8702952 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -25,6 +25,7 @@
 public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
     private final Handler mHandler;
     private final DownloadStateCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
 
     public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
         mAppCallback = appCallback;
@@ -32,9 +33,13 @@
     }
 
     @Override
-    public void progress(final DownloadRequest request, final FileInfo fileInfo,
+    public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
             final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
             final int fullDecodedSize) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -45,13 +50,21 @@
     }
 
     @Override
-    public void state(final DownloadRequest request, final FileInfo fileInfo, final int state)
-            throws RemoteException {
+    public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            final int state) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStateChanged(request, fileInfo, state);
+                mAppCallback.onStateUpdated(request, fileInfo, state);
             }
         });
     }
+
+    public void stop() {
+        mIsStopped = true;
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index 28ee5f1..eb6579ce 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -31,7 +31,7 @@
     }
 
     @Override
-    public void onError(int errorCode, String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
         if (mIsStopped) {
             return;
         }
@@ -45,7 +45,7 @@
     }
 
     @Override
-    public void onStreamStateUpdated(int state, int reason) throws RemoteException {
+    public void onStreamStateUpdated(final int state, final int reason) throws RemoteException {
         if (mIsStopped) {
             return;
         }
@@ -73,7 +73,7 @@
     }
 
     @Override
-    public void onBroadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+    public void onBroadcastSignalStrengthUpdated(final int signalStrength) throws RemoteException {
         if (mIsStopped) {
             return;
         }
@@ -87,7 +87,7 @@
     }
 
     @Override
-    public void onStreamMethodUpdated(int methodType) throws RemoteException {
+    public void onStreamMethodUpdated(final int methodType) throws RemoteException {
         if (mIsStopped) {
             return;
         }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index 0d890b8..d782d12 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -34,7 +34,7 @@
     }
 
     @Override
-    public void onError(int errorCode, String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
         if (mIsStopped) {
             return;
         }
@@ -48,7 +48,7 @@
     }
 
     @Override
-    public void onStreamingServicesUpdated(List<StreamingServiceInfo> services)
+    public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services)
             throws RemoteException {
         if (mIsStopped) {
             return;
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 6a3e852..0a71921 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -25,7 +25,7 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
-import android.telephony.MbmsDownloadManager;
+import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.vendor.VendorUtils;
 import android.util.Log;
 
@@ -148,7 +148,7 @@
 
     private boolean verifyIntentContents(Context context, Intent intent) {
         if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT)) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT)) {
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
@@ -160,7 +160,7 @@
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO)) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO)) {
                 Log.w(LOG_TAG, "Download result did not include the associated file info. " +
                         "Ignoring.");
                 return false;
@@ -213,11 +213,11 @@
         DownloadRequest request = intent.getParcelableExtra(VendorUtils.EXTRA_REQUEST);
         Intent intentForApp = request.getIntentForApp();
 
-        int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT,
-                MbmsDownloadManager.RESULT_CANCELLED);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT, result);
+        int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+                MbmsDownloadSession.RESULT_CANCELLED);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
 
-        if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) {
+        if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
             Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
             context.sendBroadcast(intentForApp);
             return;
@@ -232,7 +232,7 @@
         }
 
         FileInfo completedFileInfo =
-                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO);
+                (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
         String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
 
         Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
@@ -241,8 +241,8 @@
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_COMPLETED_FILE_URI, finalFileLocation);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO, completedFileInfo);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
         setResultCode(RESULT_OK);
@@ -511,7 +511,7 @@
     private String getMiddlewarePackageCached(Context context) {
         if (mMiddlewarePackageNameCache == null) {
             mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
-                    MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
+                    MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
         }
         return mMiddlewarePackageNameCache;
     }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
similarity index 84%
rename from telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
rename to telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
index 24c039e..f845132 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -16,7 +16,7 @@
 
 package android.telephony.mbms;
 
-import android.telephony.MbmsDownloadManager;
+import android.telephony.MbmsDownloadSession;
 
 import java.util.List;
 
@@ -24,7 +24,7 @@
  * A callback class that apps should use to receive information on file downloads over
  * cell-broadcast.
  */
-public class MbmsDownloadManagerCallback {
+public class MbmsDownloadSessionCallback {
 
     /**
      * Indicates that the middleware has encountered an asynchronous error.
@@ -41,8 +41,8 @@
      *
      * This will only be called after the application has requested a list of file services and
      * specified a service class list of interest via
-     * {@link MbmsDownloadManager#getFileServices(List)}. If there are subsequent calls to
-     * {@link MbmsDownloadManager#getFileServices(List)}, this method may not be called again if
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)}. If there are subsequent calls to
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)}, this method may not be called again if
      * the list of service classes would remain the same.
      *
      * @param services The most recently updated list of available file services.
@@ -55,7 +55,7 @@
      * Called to indicate that the middleware has been initialized and is ready.
      *
      * Before this method is called, calling any method on an instance of
-     * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
+     * {@link MbmsDownloadSession} will result in an {@link MbmsException}
      * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
      * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 5b069cb..e05bd42 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -32,7 +32,7 @@
 
     /**
      * Indicates that the app attempted to perform an operation on an instance of
-     * TODO link android.telephony.MbmsDownloadManager or
+     * {@link android.telephony.MbmsDownloadSession} or
      * {@link MbmsStreamingSession} without being bound to the middleware.
      */
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
@@ -48,8 +48,7 @@
         private InitializationErrors() {}
         /**
          * Indicates that the app tried to create more than one instance each of
-         * {@link MbmsStreamingSession} or
-         * TODO link android.telephony.MbmsDownloadManager
+         * {@link MbmsStreamingSession} or {@link android.telephony.MbmsDownloadSession}.
          */
         public static final int ERROR_DUPLICATE_INITIALIZE = 101;
         /** Indicates that the app is not authorized to access media via MBMS.*/
@@ -67,7 +66,7 @@
         /**
          * Indicates that the app attempted to perform an operation before receiving notification
          * that the middleware is ready via {@link MbmsStreamingSessionCallback#onMiddlewareReady()}
-         * or TODO: link MbmsDownloadManagerCallback#middlewareReady
+         * or {@link MbmsDownloadSessionCallback#onMiddlewareReady()}.
          */
         public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
         /**
diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
index 190ec8b..689becd 100644
--- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
+++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
@@ -27,7 +27,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
-import android.telephony.MbmsDownloadManager;
+import android.telephony.MbmsDownloadSession;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -181,7 +181,7 @@
                 return new File(storedTempFileRoot).getCanonicalFile();
             } else {
                 return new File(context.getFilesDir(),
-                        MbmsDownloadManager.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
+                        MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
             }
         } catch (IOException e) {
             throw new RuntimeException("Unable to canonicalize temp file root path " + e);
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index e5d1d3b..351147a6 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.telephony.mbms;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -31,7 +33,7 @@
 
 /**
  * Describes a cell-broadcast service. This class should not be instantiated directly -- use
- * {@link StreamingServiceInfo} or TODO link FileServiceInfo
+ * {@link StreamingServiceInfo} or {@link FileServiceInfo}
  */
 public class ServiceInfo {
     // arbitrary limit on the number of locale -> name pairs we support
@@ -58,6 +60,13 @@
         if (newLocales.size() > MAP_LIMIT) {
             throw new RuntimeException("bad locales length " + newLocales.size());
         }
+
+        for (Locale l : newLocales) {
+            if (!newNames.containsKey(l)) {
+                throw new IllegalArgumentException("A name must be provided for each locale");
+            }
+        }
+
         names = new HashMap(newNames.size());
         names.putAll(newNames);
         className = newClassName;
@@ -114,10 +123,19 @@
     }
 
     /**
-     * User displayable names listed by language. Do not modify the map returned from this method.
+     * Get the user-displayable name for this cell-broadcast service corresponding to the
+     * provided {@link Locale}.
+     * @param locale The {@link Locale} in which you want the name of the service. This must be a
+     *               value from the list returned by {@link #getLocales()} -- an
+     *               {@link IllegalArgumentException} may be thrown otherwise.
+     * @return The {@link CharSequence} providing the name of the service in the given
+     *         {@link Locale}
      */
-    public Map<Locale, String> getNames() {
-        return names;
+    public @NonNull CharSequence getNameForLocale(@NonNull Locale locale) {
+        if (!names.containsKey(locale)) {
+            throw new IllegalArgumentException("Locale not supported");
+        }
+        return names.get(locale);
     }
 
     /**
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index f29499d..ed5e826 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -20,7 +20,7 @@
 import android.net.Uri;
 import android.telephony.mbms.DownloadRequest;
 import android.telephony.mbms.FileInfo;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
 import android.telephony.mbms.IDownloadStateCallback;
 
 /**
@@ -28,13 +28,18 @@
  */
 interface IMbmsDownloadService
 {
-    int initialize(int subId, IMbmsDownloadManagerCallback listener);
+    int initialize(int subId, IMbmsDownloadSessionCallback listener);
 
-    int getFileServices(int subId, in List<String> serviceClasses);
+    int requestUpdateFileServices(int subId, in List<String> serviceClasses);
 
     int setTempFileRootDirectory(int subId, String rootDirectoryPath);
 
-    int download(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+    int download(in DownloadRequest downloadRequest);
+
+    int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+
+    int unregisterStateCallback(in DownloadRequest downloadRequest,
+        IDownloadStateCallback listener);
 
     List<DownloadRequest> listPendingDownloads(int subscriptionId);
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index f38c93c..a3c8426 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -20,17 +20,21 @@
 import android.annotation.SystemApi;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
 import android.telephony.mbms.IDownloadStateCallback;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsException;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Base class for MbmsDownloadService. The middleware should return an instance of this object from
@@ -39,6 +43,9 @@
  */
 @SystemApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+    private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+    private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
     /**
      * Initialize the download service for this app and subId, registering the listener.
      *
@@ -48,12 +55,12 @@
      *
      * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
      * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
-     * {@link IMbmsDownloadManagerCallback#error(int, String)}.
+     * {@link IMbmsDownloadSessionCallback#onError(int, String)}.
      *
      * @param callback The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
      */
-    public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+    public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback)
             throws RemoteException {
         return 0;
     }
@@ -64,7 +71,7 @@
      */
     @Override
     public final int initialize(final int subscriptionId,
-            final IMbmsDownloadManagerCallback callback) throws RemoteException {
+            final IMbmsDownloadSessionCallback callback) throws RemoteException {
         final int uid = Binder.getCallingUid();
         callback.asBinder().linkToDeath(new DeathRecipient() {
             @Override
@@ -73,11 +80,11 @@
             }
         }, 0);
 
-        return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+        return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
             @Override
             public void onError(int errorCode, String message) {
                 try {
-                    callback.error(errorCode, message);
+                    callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -86,7 +93,7 @@
             @Override
             public void onFileServicesUpdated(List<FileServiceInfo> services) {
                 try {
-                    callback.fileServicesUpdated(services);
+                    callback.onFileServicesUpdated(services);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -95,7 +102,7 @@
             @Override
             public void onMiddlewareReady() {
                 try {
-                    callback.middlewareReady();
+                    callback.onMiddlewareReady();
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -106,7 +113,7 @@
     /**
      * 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)}
+     * later via {@link IMbmsDownloadSessionCallback#onFileServicesUpdated(List)}
      *
      * Note that subsequent calls with the same uid and subId will replace
      * the service class list.
@@ -121,7 +128,7 @@
      *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
      */
     @Override
-    public int getFileServices(int subscriptionId, List<String> serviceClasses)
+    public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses)
             throws RemoteException {
         return 0;
     }
@@ -155,12 +162,32 @@
      * this is not the case, an {@link IllegalStateException} may be thrown.
      *
      * @param downloadRequest An object describing the set of files to be downloaded.
-     * @param callback A callback through which the middleware can provide progress updates to
-     *                 the app while both are still running.
      * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
      *         or {@link MbmsException#SUCCESS}
      */
-    public int download(DownloadRequest downloadRequest, DownloadStateCallback callback) {
+    @Override
+    public int download(DownloadRequest downloadRequest) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Registers a download state callbacks for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the progress or
+     * status of the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     *
+     * {@link DownloadRequest} in the past,
+     * {@link android.telephony.mbms.MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param callback The callback object to use.
+     */
+    public int registerStateCallback(DownloadRequest downloadRequest,
+            DownloadStateCallback callback) throws RemoteException {
         return 0;
     }
 
@@ -169,36 +196,101 @@
      * @hide
      */
     @Override
-    public final int download(DownloadRequest downloadRequest, IDownloadStateCallback callback)
+    public final int registerStateCallback(
+            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
             throws RemoteException {
         final int uid = Binder.getCallingUid();
-        callback.asBinder().linkToDeath(new DeathRecipient() {
+        DeathRecipient deathRecipient = new DeathRecipient() {
             @Override
             public void binderDied() {
                 onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                mDownloadCallbackBinderMap.remove(callback.asBinder());
+                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
             }
-        }, 0);
+        };
+        mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
+        callback.asBinder().linkToDeath(deathRecipient, 0);
 
-        return download(downloadRequest, new DownloadStateCallback() {
+        DownloadStateCallback exposedCallback = new DownloadStateCallback() {
             @Override
             public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
                     currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
                     fullDecodedSize) {
                 try {
-                    callback.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
+                    callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize,
                             currentDecodedSize, fullDecodedSize);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
                 }
             }
-        });
+
+            @Override
+            public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+                    @MbmsDownloadSession.DownloadStatus int state) {
+                try {
+                    callback.onStateUpdated(request, fileInfo, state);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                }
+            }
+        };
+
+        mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+
+        return registerStateCallback(downloadRequest, exposedCallback);
     }
 
+    /**
+     * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link android.telephony.mbms.MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param callback The callback object that
+     *                 {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+     *                 was called with.
+     */
+    public int unregisterStateCallback(DownloadRequest downloadRequest,
+            DownloadStateCallback callback) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int unregisterStateCallback(
+            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+            throws RemoteException {
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown callback");
+        }
+
+        callback.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadStateCallback exposedCallback =
+                mDownloadCallbackBinderMap.remove(callback.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown callback");
+        }
+
+        return unregisterStateCallback(downloadRequest, exposedCallback);
+    }
 
     /**
      * 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, DownloadStateCallback)} but not cancelled through
+     * {@link #download(DownloadRequest)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * The middleware must return a non-null result synchronously or throw an exception
      * inheriting from {@link RuntimeException}.
@@ -232,7 +324,7 @@
      *
      * If the middleware has not yet been properly initialized or if it has no records of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned.
+     * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
      *
      * @param downloadRequest The download request to query.
      * @param fileInfo The particular file within the request to get information on.
@@ -266,7 +358,7 @@
      * Signals that the app wishes to dispose of the session identified by the
      * {@code subscriptionId} argument and the caller's uid. No notification back to the
      * app is required for this operation, and the corresponding callback provided via
-     * {@link #initialize(int, IMbmsDownloadManagerCallback)} should no longer be used
+     * {@link #initialize(int, IMbmsDownloadSessionCallback)} should no longer be used
      * after this method has been called by the app.
      *
      * Any download requests issued by the app should remain in effect until the app calls
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 1a4d0d8..7f57dde 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -75,7 +75,7 @@
 
         return initialize(new MbmsStreamingSessionCallback() {
             @Override
-            public void onError(int errorCode, String message) {
+            public void onError(final int errorCode, final String message) {
                 try {
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
@@ -84,7 +84,7 @@
             }
 
             @Override
-            public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+            public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
                 try {
                     callback.onStreamingServicesUpdated(services);
                 } catch (RemoteException e) {
@@ -150,8 +150,8 @@
      * @hide
      */
     @Override
-    public int startStreaming(int subscriptionId, String serviceId,
-            IStreamingServiceCallback callback) throws RemoteException {
+    public int startStreaming(final int subscriptionId, String serviceId,
+            final IStreamingServiceCallback callback) throws RemoteException {
         final int uid = Binder.getCallingUid();
         callback.asBinder().linkToDeath(new DeathRecipient() {
             @Override
@@ -162,7 +162,7 @@
 
         return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
             @Override
-            public void onError(int errorCode, String message) {
+            public void onError(final int errorCode, final String message) {
                 try {
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
@@ -171,8 +171,8 @@
             }
 
             @Override
-            public void onStreamStateUpdated(@StreamingService.StreamingState int state,
-                    @StreamingService.StreamingStateChangeReason int reason) {
+            public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
+                    @StreamingService.StreamingStateChangeReason final int reason) {
                 try {
                     callback.onStreamStateUpdated(state, reason);
                 } catch (RemoteException e) {
@@ -190,7 +190,7 @@
             }
 
             @Override
-            public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+            public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
                 try {
                     callback.onBroadcastSignalStrengthUpdated(signalStrength);
                 } catch (RemoteException e) {
@@ -199,7 +199,7 @@
             }
 
             @Override
-            public void onStreamMethodUpdated(int methodType) {
+            public void onStreamMethodUpdated(final int methodType) {
                 try {
                     callback.onStreamMethodUpdated(methodType);
                 } catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index 7b54aa8..923ea15 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.DownloadRequest;
 import android.telephony.mbms.MbmsDownloadReceiver;
 
@@ -39,8 +40,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_MBMS_DOWNLOAD_RESULT}
-     * {@link android.telephony.MbmsDownloadManager#EXTRA_MBMS_FILE_INFO}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
      * {@link #EXTRA_REQUEST}
      * {@link #EXTRA_TEMP_LIST}
      * {@link #EXTRA_FINAL_URI}