Merge changes from topic "embms-cherry-pick-3" into oc-mr1-dev

* changes:
  API council suggested edits, part 3
  Apply suggested edits to the file-download API
  Make changes to MBMS Streaming APIs per recommendations
This commit is contained in:
Hall Liu
2017-09-15 17:17:38 +00:00
committed by Android (Google) Code Review
28 changed files with 834 additions and 632 deletions

View File

@@ -502,8 +502,8 @@ LOCAL_SRC_FILES += \
telecomm/java/com/android/internal/telecom/IInCallService.aidl \ telecomm/java/com/android/internal/telecom/IInCallService.aidl \
telecomm/java/com/android/internal/telecom/ITelecomService.aidl \ telecomm/java/com/android/internal/telecom/ITelecomService.aidl \
telecomm/java/com/android/internal/telecom/RemoteServiceCallback.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/IMbmsStreamingManagerCallback.aidl \ telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl \
telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \ telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \
telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \ telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \
telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl \ telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl \

View File

@@ -34,11 +34,11 @@ import android.os.RemoteException;
import android.telephony.mbms.DownloadStateCallback; import android.telephony.mbms.DownloadStateCallback;
import android.telephony.mbms.FileInfo; import android.telephony.mbms.FileInfo;
import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.InternalDownloadManagerCallback; import android.telephony.mbms.InternalDownloadSessionCallback;
import android.telephony.mbms.InternalDownloadStateCallback; import android.telephony.mbms.InternalDownloadStateCallback;
import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsDownloadReceiver;
import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsTempFileProvider; import android.telephony.mbms.MbmsTempFileProvider;
import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.vendor.IMbmsDownloadService; import android.telephony.mbms.vendor.IMbmsDownloadService;
@@ -48,7 +48,10 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -58,8 +61,8 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
* This class provides functionality for file download over MBMS. * This class provides functionality for file download over MBMS.
* @hide * @hide
*/ */
public class MbmsDownloadManager { public class MbmsDownloadSession implements AutoCloseable {
private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName(); private static final String LOG_TAG = MbmsDownloadSession.class.getSimpleName();
/** /**
* Service action which must be handled by the middleware implementing the MBMS file download * Service action which must be handled by the middleware implementing the MBMS file download
@@ -95,16 +98,28 @@ public class MbmsDownloadManager {
/** /**
* {@link Uri} extra that Android will attach to the intent supplied via * {@link Uri} extra that Android will attach to the intent supplied via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)} * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
* Indicates the location of the successfully * Indicates the location of the successfully downloaded file within the temp file root set
* downloaded file. Will always be set to a non-null value if * via {@link #setTempFileRootDirectory(File)}.
* While you may use this file in-place, it is highly encouraged that you move
* this file to a different location after receiving the download completion intent, as this
* file resides within the temp file directory.
*
* Will always be set to a non-null value if
* {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}. * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
*/ */
public static final String EXTRA_MBMS_COMPLETED_FILE_URI = public static final String EXTRA_MBMS_COMPLETED_FILE_URI =
"android.telephony.extra.MBMS_COMPLETED_FILE_URI"; "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
/**
* Extra containing the {@link DownloadRequest} for which the download result or file
* descriptor request is for. Must not be null.
*/
public static final String EXTRA_MBMS_DOWNLOAD_REQUEST =
"android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
/** /**
* The default directory name for all MBMS temp files. If you call * 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 * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
* path returned by {@link Context#getFilesDir()}. * path returned by {@link Context#getFilesDir()}.
*/ */
@@ -173,98 +188,88 @@ public class MbmsDownloadManager {
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override @Override
public void binderDied() { public void binderDied() {
sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification"); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
} }
}; };
private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null); 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) { int subscriptionId, Handler handler) {
mContext = context; mContext = context;
mSubscriptionId = subscriptionId; mSubscriptionId = subscriptionId;
if (handler == null) { 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 * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
* {@link Handler} * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
* See {@link #create(Context, MbmsDownloadManagerCallback, int, Handler)}
*/ */
public static MbmsDownloadManager create(Context context, public static MbmsDownloadSession create(@NonNull Context context,
MbmsDownloadManagerCallback callback) @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
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 {
return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), 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. * 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 * 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 MbmsDownloadSession} that is returned will not be ready for use until
* {@link MbmsDownloadManagerCallback#onMiddlewareReady()} is called on the provided callback. * {@link MbmsDownloadSessionCallback#onMiddlewareReady()} is called on the provided callback.
* If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown. * 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 * You may only have one instance of {@link MbmsDownloadSession} per UID. If you call this
* method while there is an active instance of {@link MbmsDownloadManager} in your process * method while there is an active instance of {@link MbmsDownloadSession} in your process
* (in other words, one that has not had {@link #dispose()} called on it), this method will * (in other words, one that has not had {@link #close()} called on it), this method will
* throw an {@link MbmsException}. If you call this method in a different process * throw an {@link IllegalStateException}. If you call this method in a different process
* running under the same UID, an error will be indicated via * 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 * 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 * receive such an asynchronous error, you must call {@link #close()} on the instance of
* {@link MbmsDownloadManager} that you received before calling this method again. * {@link MbmsDownloadSession} that you received before calling this method again.
* *
* @param context The instance of {@link Context} to use * @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 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, public static @Nullable MbmsDownloadSession create(@NonNull Context context,
MbmsDownloadManagerCallback listener, int subscriptionId, Handler handler) final @NonNull MbmsDownloadSessionCallback callback,
throws MbmsException { int subscriptionId, @NonNull Handler handler) {
if (!sIsInitialized.compareAndSet(false, true)) { if (!sIsInitialized.compareAndSet(false, true)) {
throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE); throw new IllegalStateException("Cannot have two active instances");
} }
MbmsDownloadManager mdm = MbmsDownloadSession session =
new MbmsDownloadManager(context, listener, subscriptionId, handler); new MbmsDownloadSession(context, callback, subscriptionId, handler);
try { final int result = session.bindAndInitialize();
mdm.bindAndInitialize(); if (result != MbmsErrors.SUCCESS) {
} catch (MbmsException e) {
sIsInitialized.set(false); 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 { private int bindAndInitialize() {
MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
new ServiceConnection() { new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
@@ -280,12 +285,12 @@ public class MbmsDownloadManager {
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(LOG_TAG, "Runtime exception during initialization"); Log.e(LOG_TAG, "Runtime exception during initialization");
sendErrorToApp( sendErrorToApp(
MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString()); e.toString());
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
} }
if (result != MbmsException.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization"); sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
@@ -293,7 +298,7 @@ public class MbmsDownloadManager {
try { try {
downloadService.asBinder().linkToDeath(mDeathRecipient, 0); downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) { } catch (RemoteException e) {
sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
"Middleware lost during initialization"); "Middleware lost during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
@@ -313,39 +318,35 @@ public class MbmsDownloadManager {
* An inspection API to retrieve the list of available * An inspection API to retrieve the list of available
* {@link android.telephony.mbms.FileServiceInfo}s currently being advertised. * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
* The results are returned asynchronously via a call to * 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 * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
* negotiated beforehand between the app and the carrier. * 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: * May throw an {@link IllegalStateException} or {@link IllegalArgumentException}.
* {@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}
* *
* @param classList A list of service classes which the app wishes to receive * @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 * about. Subsequent calls to this method will replace this list of service
* classes (i.e. the middleware will no longer send updates for services * classes (i.e. the middleware will no longer send updates for services
* matching classes only in the old list). * 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(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
int returnCode = downloadService.getFileServices(mSubscriptionId, classList); int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
if (returnCode != MbmsException.SUCCESS) { if (returnCode != MbmsErrors.SUCCESS) {
throw new MbmsException(returnCode); sendErrorToApp(returnCode, null);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); Log.w(LOG_TAG, "Remote process died");
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} }
} }
@@ -357,31 +358,32 @@ public class MbmsDownloadManager {
* local instance of {@link android.content.SharedPreferences} and by the middleware. * local instance of {@link android.content.SharedPreferences} and by the middleware.
* *
* If this method is not called at least once before calling * 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 * 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 * Before calling this method, the app must cancel all of its pending
* {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done, * {@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 * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
* provided directory is the same as what has been previously configured. * 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 * 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. * @param tempFileRootDirectory A directory to place temp files in.
*/ */
public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) {
throws MbmsException {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
if (!tempFileRootDirectory.exists()) { try {
throw new IllegalArgumentException("Provided directory does not exist"); validateTempFileRootSanity(tempFileRootDirectory);
} } catch (IOException e) {
if (!tempFileRootDirectory.isDirectory()) { throw new IllegalStateException("Got IOException checking directory sanity");
throw new IllegalArgumentException("Provided File is not a directory");
} }
String filePath; String filePath;
try { try {
@@ -392,12 +394,13 @@ public class MbmsDownloadManager {
try { try {
int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath); int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
if (result != MbmsException.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
throw new MbmsException(result); sendErrorToApp(result, null);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
SharedPreferences prefs = mContext.getSharedPreferences( SharedPreferences prefs = mContext.getSharedPreferences(
@@ -405,10 +408,28 @@ public class MbmsDownloadManager {
prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply(); 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 * Retrieves the currently configured temp file root directory. Returns the file that was
* configured via {@link #setTempFileRootDirectory(File)} or the default directory * 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 * 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}. * shared preferences were reset, returns {@code null}.
* *
@@ -426,31 +447,24 @@ public class MbmsDownloadManager {
} }
/** /**
* 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} * May throw an {@link IllegalArgumentException}
* *
* If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed, * 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 * 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. * file root directory.
* *
* Asynchronous errors through the listener include any of the errors * 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 * @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)}
*/ */
public void download(DownloadRequest request, @Nullable DownloadStateCallback stateCallback, public void download(@NonNull DownloadRequest request) {
Handler handler)
throws MbmsException {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { 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. // Check to see whether the app's set a temp root dir yet, and set it if not.
@@ -462,67 +476,151 @@ public class MbmsDownloadManager {
tempRootDirectory.mkdirs(); tempRootDirectory.mkdirs();
setTempFileRootDirectory(tempRootDirectory); setTempFileRootDirectory(tempRootDirectory);
} }
InternalDownloadStateCallback internalCallback = null;
if (stateCallback != null) {
internalCallback = new InternalDownloadStateCallback(stateCallback,
handler == null ? mInternalCallback.getHandler() : handler);
}
checkValidDownloadDestination(request);
writeDownloadRequestToken(request); writeDownloadRequestToken(request);
try { try {
downloadService.download(request, internalCallback); downloadService.download(request);
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} }
} }
/** /**
* Returns a list of pending {@link DownloadRequest}s that originated from this application. * Returns a list of pending {@link DownloadRequest}s that originated from this application.
* A pending request is one that was issued via * 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)}. * {@link #cancelDownload(DownloadRequest)}.
* @return A list, possibly empty, of {@link DownloadRequest}s * @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(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
return downloadService.listPendingDownloads(mSubscriptionId); return downloadService.listPendingDownloads(mSubscriptionId);
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.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 != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
sendErrorToApp(result, null);
return;
}
} catch (RemoteException e) {
mService.set(null);
sendErrorToApp(MbmsErrors.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 != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
sendErrorToApp(result, null);
}
} catch (RemoteException e) {
mService.set(null);
sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
}
} finally {
InternalDownloadStateCallback internalCallback =
mInternalDownloadCallbacks.remove(callback);
if (internalCallback != null) {
internalCallback.stop();
}
} }
} }
/** /**
* Attempts to cancel the specified {@link DownloadRequest}. * Attempts to cancel the specified {@link DownloadRequest}.
* *
* If the middleware is not aware of the specified download request, an MbmsException will be * If the middleware is not aware of the specified download request,
* thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_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. * @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(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
int result = downloadService.cancelDownload(downloadRequest); int result = downloadService.cancelDownload(downloadRequest);
if (result != MbmsException.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
throw new MbmsException(result); if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
sendErrorToApp(result, null);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
deleteDownloadRequestToken(downloadRequest); deleteDownloadRequestToken(downloadRequest);
} }
@@ -530,7 +628,7 @@ public class MbmsDownloadManager {
/** /**
* Gets information about the status of a file pending download. * 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}, * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
* {@link #STATUS_UNKNOWN} will be returned. * {@link #STATUS_UNKNOWN} will be returned.
* *
@@ -539,18 +637,18 @@ public class MbmsDownloadManager {
* @return The status of the download. * @return The status of the download.
*/ */
@DownloadStatus @DownloadStatus
public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
throws MbmsException {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
return downloadService.getDownloadStatus(downloadRequest, fileInfo); return downloadService.getDownloadStatus(downloadRequest, fileInfo);
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return STATUS_UNKNOWN;
} }
} }
@@ -566,30 +664,50 @@ public class MbmsDownloadManager {
* when available. * when available.
* This will not interrupt in-progress downloads. * This will not interrupt in-progress downloads.
* *
* If the middleware is not aware of the specified download request, an MbmsException will be * This is distinct from cancelling and re-issuing the download request -- if you cancel and
* thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. * re-issue, the middleware will not clear its cache of download state information.
*
* If the middleware is not aware of the specified download request, an
* {@link IllegalArgumentException} will be thrown.
* *
* May throw a {@link MbmsException} with error code
* @param downloadRequest The request to re-download files for. * @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(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
int result = downloadService.resetDownloadKnowledge(downloadRequest); int result = downloadService.resetDownloadKnowledge(downloadRequest);
if (result != MbmsException.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
throw new MbmsException(result); if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
sendErrorToApp(result, null);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.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 { try {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
@@ -603,6 +721,7 @@ public class MbmsDownloadManager {
} finally { } finally {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
mInternalCallback.stop();
} }
} }
@@ -645,35 +764,9 @@ public class MbmsDownloadManager {
return new File(tempFileLocation, downloadTokenFileName); return new File(tempFileLocation, downloadTokenFileName);
} }
/**
* Verifies the following:
* If a request is multi-part,
* 1. Destination Uri must exist and be a directory
* 2. Directory specified must contain no files.
* Otherwise
* 1. The file specified by the destination Uri must not exist.
*/
private void checkValidDownloadDestination(DownloadRequest request) {
File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
if (request.isMultipartDownload()) {
if (!toFile.isDirectory()) {
throw new IllegalArgumentException("Multipart download must specify valid " +
"destination directory.");
}
if (toFile.listFiles().length > 0) {
throw new IllegalArgumentException("Destination directory must be clear of all " +
"files.");
}
} else {
if (toFile.exists()) {
throw new IllegalArgumentException("Destination file must not exist.");
}
}
}
private void sendErrorToApp(int errorCode, String message) { private void sendErrorToApp(int errorCode, String message) {
try { try {
mInternalCallback.error(errorCode, message); mInternalCallback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
// Ignore, should not happen locally. // Ignore, should not happen locally.
} }

View File

@@ -16,6 +16,8 @@
package android.telephony; package android.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.content.ComponentName; import android.content.ComponentName;
@@ -25,18 +27,20 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.InternalStreamingManagerCallback; import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback; import android.telephony.mbms.InternalStreamingServiceCallback;
import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsStreamingManagerCallback; import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.StreamingService; import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback; import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo; import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService; import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -46,8 +50,8 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
* This class provides functionality for streaming media over MBMS. * This class provides functionality for streaming media over MBMS.
* @hide * @hide
*/ */
public class MbmsStreamingManager { public class MbmsStreamingSession implements AutoCloseable {
private static final String LOG_TAG = "MbmsStreamingManager"; private static final String LOG_TAG = "MbmsStreamingSession";
/** /**
* Service action which must be handled by the middleware implementing the MBMS streaming * Service action which must be handled by the middleware implementing the MBMS streaming
@@ -66,98 +70,98 @@ public class MbmsStreamingManager {
@Override @Override
public void binderDied() { public void binderDied() {
sIsInitialized.set(false); sIsInitialized.set(false);
sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification"); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
} }
}; };
private InternalStreamingManagerCallback mInternalCallback; private InternalStreamingSessionCallback mInternalCallback;
private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
private final Context mContext; private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */ /** @hide */
private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback, private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
int subscriptionId, Handler handler) { int subscriptionId, Handler handler) {
mContext = context; mContext = context;
mSubscriptionId = subscriptionId; mSubscriptionId = subscriptionId;
if (handler == null) { if (handler == null) {
handler = new Handler(Looper.getMainLooper()); handler = new Handler(Looper.getMainLooper());
} }
mInternalCallback = new InternalStreamingManagerCallback(callback, handler); mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
} }
/** /**
* Create a new MbmsStreamingManager using the given subscription ID. * Create a new {@link MbmsStreamingSession} using the given subscription ID.
* *
* Note that this call will bind a remote service. You may not call this method on your app's * Note that this call will bind a remote service. You may not call this method on your app's
* main thread. This may throw an {@link MbmsException}, indicating errors that may happen * main thread.
* during the initialization or binding process.
* *
* * You may only have one instance of {@link MbmsStreamingSession} per UID. If you call this
* You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this * method while there is an active instance of {@link MbmsStreamingSession} in your process
* method while there is an active instance of {@link MbmsStreamingManager} in your process * (in other words, one that has not had {@link #close()} called on it), this method will
* (in other words, one that has not had {@link #dispose()} called on it), this method will * throw an {@link IllegalStateException}. If you call this method in a different process
* throw an {@link MbmsException}. If you call this method in a different process
* running under the same UID, an error will be indicated via * running under the same UID, an error will be indicated via
* {@link MbmsStreamingManagerCallback#onError(int, String)}. * {@link MbmsStreamingSessionCallback#onError(int, String)}.
* *
* Note that initialization may fail asynchronously. If you wish to try again after you * 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 * receive such an asynchronous error, you must call {@link #close()} on the instance of
* {@link MbmsStreamingManager} that you received before calling this method again. * {@link MbmsStreamingSession} that you received before calling this method again.
* *
* @param context The {@link Context} to use. * @param context The {@link Context} to use.
* @param callback A callback object on which you wish to receive results of asynchronous * @param callback A callback object on which you wish to receive results of asynchronous
* operations. * operations.
* @param subscriptionId The subscription ID to use. * @param subscriptionId The subscription ID to use.
* @param handler The handler you wish to receive callbacks on. If null, callbacks will be * @param handler The handler you wish to receive callbacks on.
* processed on the main looper (in other words, the looper returned from * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
* {@link Looper#getMainLooper()}).
*/ */
public static MbmsStreamingManager create(Context context, public static @Nullable MbmsStreamingSession create(@NonNull Context context,
MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler) final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
throws MbmsException { @NonNull Handler handler) {
if (!sIsInitialized.compareAndSet(false, true)) { if (!sIsInitialized.compareAndSet(false, true)) {
throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE); throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
} }
MbmsStreamingManager manager = new MbmsStreamingManager(context, callback, MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
subscriptionId, handler); subscriptionId, handler);
try {
manager.bindAndInitialize(); final int result = session.bindAndInitialize();
} catch (MbmsException e) { if (result != MbmsErrors.SUCCESS) {
sIsInitialized.set(false); sIsInitialized.set(false);
throw e; handler.post(new Runnable() {
@Override
public void run() {
callback.onError(result, null);
}
});
return null;
} }
return manager; return session;
} }
/** /**
* Create a new MbmsStreamingManager using the system default data subscription ID. * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
* See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}. * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
*/ */
public static MbmsStreamingManager create(Context context, public static MbmsStreamingSession create(@NonNull Context context,
MbmsStreamingManagerCallback callback, Handler handler) @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
throws MbmsException {
return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler); return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
} }
/** /**
* Create a new MbmsStreamingManager using the system default data subscription ID and * Terminates this instance. Also terminates
* default {@link Handler}. * any streaming services spawned from this instance as if
* See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}. * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
*/ * no further callbacks originating from the middleware will be enqueued on the provided
public static MbmsStreamingManager create(Context context, * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
MbmsStreamingManagerCallback callback) * enqueued will still be delivered.
throws MbmsException { *
return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null); * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
} * obtain another instance of {@link MbmsStreamingSession} immediately after this method
* returns.
/**
* Terminates this instance, ending calls to the registered listener. Also terminates
* any streaming services spawned from this instance.
* *
* May throw an {@link IllegalStateException} * May throw an {@link IllegalStateException}
*/ */
public void dispose() { public void close() {
try { try {
IMbmsStreamingService streamingService = mService.get(); IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) { if (streamingService == null) {
@@ -165,47 +169,49 @@ public class MbmsStreamingManager {
return; return;
} }
streamingService.dispose(mSubscriptionId); streamingService.dispose(mSubscriptionId);
for (StreamingService s : mKnownActiveStreamingServices) {
s.getCallback().stop();
}
mKnownActiveStreamingServices.clear();
} catch (RemoteException e) { } catch (RemoteException e) {
// Ignore for now // Ignore for now
} finally { } finally {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
mInternalCallback.stop();
} }
} }
/** /**
* An inspection API to retrieve the list of streaming media currently be advertised. * An inspection API to retrieve the list of streaming media currently be advertised.
* The results are returned asynchronously through the previously registered callback. * The results are returned asynchronously via
* serviceClasses lets the app filter on types of programming and is opaque data between * {@link MbmsStreamingSessionCallback#onStreamingServicesUpdated(List)} on the callback
* the app and the carrier. * provided upon creation.
* *
* Multiple calls replace the list of serviceClasses of interest. * Multiple calls replace the list of service classes of interest.
* *
* This may throw an {@link MbmsException} containing any error in * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
* {@link android.telephony.mbms.MbmsException.GeneralErrors},
* {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
* {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
* *
* May also throw an unchecked {@link IllegalArgumentException} or an * @param serviceClassList A list of streaming service classes that the app would like updates
* {@link IllegalStateException} * on. The exact names of these classes should be negotiated with the
* * wireless carrier separately.
* @param classList A list of streaming service classes that the app would like updates on.
*/ */
public void getStreamingServices(List<String> classList) throws MbmsException { public void requestUpdateStreamingServices(List<String> serviceClassList) {
IMbmsStreamingService streamingService = mService.get(); IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) { if (streamingService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
try { try {
int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList); int returnCode = streamingService.requestUpdateStreamingServices(
if (returnCode != MbmsException.SUCCESS) { mSubscriptionId, serviceClassList);
throw new MbmsException(returnCode); if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); Log.w(LOG_TAG, "Remote process died");
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} }
} }
@@ -216,56 +222,57 @@ public class MbmsStreamingManager {
* reported via * reported via
* {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)} * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
* *
* May throw an * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
* {@link MbmsException} containing any of the error codes in
* {@link android.telephony.mbms.MbmsException.GeneralErrors},
* {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
* {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
*
* May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
* *
* Asynchronous errors through the callback include any of the errors in * Asynchronous errors through the callback include any of the errors in
* {@link android.telephony.mbms.MbmsException.GeneralErrors} or * {@link MbmsErrors.GeneralErrors} or
* {@link android.telephony.mbms.MbmsException.StreamingErrors}. * {@link MbmsErrors.StreamingErrors}.
* *
* @param serviceInfo The information about the service to stream. * @param serviceInfo The information about the service to stream.
* @param callback A callback that'll be called when something about the stream changes. * @param callback A callback that'll be called when something about the stream changes.
* @param handler A handler that calls to {@code callback} should be called on. If null, * @param handler A handler that calls to {@code callback} should be called on.
* defaults to the handler provided via
* {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
* @return An instance of {@link StreamingService} through which the stream can be controlled. * @return An instance of {@link StreamingService} through which the stream can be controlled.
* May be {@code null} if an error occurred.
*/ */
public StreamingService startStreaming(StreamingServiceInfo serviceInfo, public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
StreamingServiceCallback callback, Handler handler) throws MbmsException { StreamingServiceCallback callback, @NonNull Handler handler) {
IMbmsStreamingService streamingService = mService.get(); IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) { if (streamingService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); throw new IllegalStateException("Middleware not yet bound");
} }
InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback( InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
callback, handler == null ? mInternalCallback.getHandler() : handler); callback, handler);
StreamingService serviceForApp = new StreamingService( StreamingService serviceForApp = new StreamingService(
mSubscriptionId, streamingService, serviceInfo, serviceCallback); mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
mKnownActiveStreamingServices.add(serviceForApp);
try { try {
int returnCode = streamingService.startStreaming( int returnCode = streamingService.startStreaming(
mSubscriptionId, serviceInfo.getServiceId(), serviceCallback); mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
if (returnCode != MbmsException.SUCCESS) { if (returnCode != MbmsErrors.SUCCESS) {
throw new MbmsException(returnCode); sendErrorToApp(returnCode, null);
return null;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); Log.w(LOG_TAG, "Remote process died");
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return null;
} }
return serviceForApp; return serviceForApp;
} }
private void bindAndInitialize() throws MbmsException { /** @hide */
MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION, public void onStreamingServiceStopped(StreamingService service) {
mKnownActiveStreamingServices.remove(service);
}
private int bindAndInitialize() {
return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
new ServiceConnection() { new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
@@ -278,19 +285,19 @@ public class MbmsStreamingManager {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization"); Log.e(LOG_TAG, "Service died before initialization");
sendErrorToApp( sendErrorToApp(
MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString()); e.toString());
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(LOG_TAG, "Runtime exception during initialization"); Log.e(LOG_TAG, "Runtime exception during initialization");
sendErrorToApp( sendErrorToApp(
MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString()); e.toString());
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
} }
if (result != MbmsException.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization"); sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
@@ -298,7 +305,7 @@ public class MbmsStreamingManager {
try { try {
streamingService.asBinder().linkToDeath(mDeathRecipient, 0); streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) { } catch (RemoteException e) {
sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
"Middleware lost during initialization"); "Middleware lost during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
@@ -316,7 +323,7 @@ public class MbmsStreamingManager {
private void sendErrorToApp(int errorCode, String message) { private void sendErrorToApp(int errorCode, String message) {
try { try {
mInternalCallback.error(errorCode, message); mInternalCallback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
// Ignore, should not happen locally. // Ignore, should not happen locally.
} }

View File

@@ -56,12 +56,10 @@ public final class DownloadRequest implements Parcelable {
/** @hide */ /** @hide */
private static class OpaqueDataContainer implements Serializable { private static class OpaqueDataContainer implements Serializable {
private final String destinationUri;
private final String appIntent; private final String appIntent;
private final int version; private final int version;
public OpaqueDataContainer(String destinationUri, String appIntent, int version) { public OpaqueDataContainer(String appIntent, int version) {
this.destinationUri = destinationUri;
this.appIntent = appIntent; this.appIntent = appIntent;
this.version = version; this.version = version;
} }
@@ -70,7 +68,6 @@ public final class DownloadRequest implements Parcelable {
public static class Builder { public static class Builder {
private String fileServiceId; private String fileServiceId;
private Uri source; private Uri source;
private Uri dest;
private int subscriptionId; private int subscriptionId;
private String appIntent; private String appIntent;
private int version = CURRENT_VERSION; private int version = CURRENT_VERSION;
@@ -104,21 +101,6 @@ public final class DownloadRequest implements Parcelable {
return this; return this;
} }
/**
* Sets the destination URI for the download request to be built. The middleware should
* not set this directly.
* @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
* final destination of the download.
*/
public Builder setDest(Uri dest) {
if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
throw new IllegalArgumentException("Destination uri must not exceed length " +
MAX_DESTINATION_URI_SIZE);
}
this.dest = dest;
return this;
}
/** /**
* Set the subscription ID on which the file(s) should be downloaded. * Set the subscription ID on which the file(s) should be downloaded.
* @param subscriptionId * @param subscriptionId
@@ -160,7 +142,6 @@ public final class DownloadRequest implements Parcelable {
OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject(); OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
version = dataContainer.version; version = dataContainer.version;
appIntent = dataContainer.appIntent; appIntent = dataContainer.appIntent;
dest = Uri.parse(dataContainer.destinationUri);
} catch (IOException e) { } catch (IOException e) {
// Really should never happen // Really should never happen
Log.e(LOG_TAG, "Got IOException trying to parse opaque data"); Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
@@ -173,24 +154,21 @@ public final class DownloadRequest implements Parcelable {
} }
public DownloadRequest build() { public DownloadRequest build() {
return new DownloadRequest(fileServiceId, source, dest, return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
subscriptionId, appIntent, version);
} }
} }
private final String fileServiceId; private final String fileServiceId;
private final Uri sourceUri; private final Uri sourceUri;
private final Uri destinationUri;
private final int subscriptionId; private final int subscriptionId;
private final String serializedResultIntentForApp; private final String serializedResultIntentForApp;
private final int version; private final int version;
private DownloadRequest(String fileServiceId, private DownloadRequest(String fileServiceId,
Uri source, Uri dest, Uri source, int sub,
int sub, String appIntent, int version) { String appIntent, int version) {
this.fileServiceId = fileServiceId; this.fileServiceId = fileServiceId;
sourceUri = source; sourceUri = source;
destinationUri = dest;
subscriptionId = sub; subscriptionId = sub;
serializedResultIntentForApp = appIntent; serializedResultIntentForApp = appIntent;
this.version = version; this.version = version;
@@ -203,7 +181,6 @@ public final class DownloadRequest implements Parcelable {
private DownloadRequest(DownloadRequest dr) { private DownloadRequest(DownloadRequest dr) {
fileServiceId = dr.fileServiceId; fileServiceId = dr.fileServiceId;
sourceUri = dr.sourceUri; sourceUri = dr.sourceUri;
destinationUri = dr.destinationUri;
subscriptionId = dr.subscriptionId; subscriptionId = dr.subscriptionId;
serializedResultIntentForApp = dr.serializedResultIntentForApp; serializedResultIntentForApp = dr.serializedResultIntentForApp;
version = dr.version; version = dr.version;
@@ -212,7 +189,6 @@ public final class DownloadRequest implements Parcelable {
private DownloadRequest(Parcel in) { private DownloadRequest(Parcel in) {
fileServiceId = in.readString(); fileServiceId = in.readString();
sourceUri = in.readParcelable(getClass().getClassLoader()); sourceUri = in.readParcelable(getClass().getClassLoader());
destinationUri = in.readParcelable(getClass().getClassLoader());
subscriptionId = in.readInt(); subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString(); serializedResultIntentForApp = in.readString();
version = in.readInt(); version = in.readInt();
@@ -225,7 +201,6 @@ public final class DownloadRequest implements Parcelable {
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
out.writeString(fileServiceId); out.writeString(fileServiceId);
out.writeParcelable(sourceUri, flags); out.writeParcelable(sourceUri, flags);
out.writeParcelable(destinationUri, flags);
out.writeInt(subscriptionId); out.writeInt(subscriptionId);
out.writeString(serializedResultIntentForApp); out.writeString(serializedResultIntentForApp);
out.writeInt(version); out.writeInt(version);
@@ -245,14 +220,6 @@ public final class DownloadRequest implements Parcelable {
return sourceUri; return sourceUri;
} }
/**
* For use by the client app only.
* @return The URI of the final destination of the download.
*/
public Uri getDestinationUri() {
return destinationUri;
}
/** /**
* @return The subscription ID on which to perform MBMS operations. * @return The subscription ID on which to perform MBMS operations.
*/ */
@@ -286,7 +253,7 @@ public final class DownloadRequest implements Parcelable {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream); ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
OpaqueDataContainer container = new OpaqueDataContainer( OpaqueDataContainer container = new OpaqueDataContainer(
destinationUri.toString(), serializedResultIntentForApp, version); serializedResultIntentForApp, version);
stream.writeObject(container); stream.writeObject(container);
stream.flush(); stream.flush();
return byteArrayOutputStream.toByteArray(); return byteArrayOutputStream.toByteArray();
@@ -352,7 +319,6 @@ public final class DownloadRequest implements Parcelable {
if (version >= 1) { if (version >= 1) {
// Hash the source URI, destination URI, and the app intent // Hash the source URI, destination URI, and the app intent
digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8)); digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8)); digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
} }
// Add updates for future versions here // Add updates for future versions here
@@ -373,13 +339,12 @@ public final class DownloadRequest implements Parcelable {
version == request.version && version == request.version &&
Objects.equals(fileServiceId, request.fileServiceId) && Objects.equals(fileServiceId, request.fileServiceId) &&
Objects.equals(sourceUri, request.sourceUri) && Objects.equals(sourceUri, request.sourceUri) &&
Objects.equals(destinationUri, request.destinationUri) &&
Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp); Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(fileServiceId, sourceUri, destinationUri, return Objects.hash(fileServiceId, sourceUri,
subscriptionId, serializedResultIntentForApp, version); subscriptionId, serializedResultIntentForApp, version);
} }
} }

View File

@@ -16,14 +16,12 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.os.Handler; import android.telephony.MbmsDownloadSession;
import android.telephony.MbmsDownloadManager;
/** /**
* A optional listener class used by download clients to track progress. Apps should extend this * A optional listener class used by download clients to track progress. Apps should extend this
* class and pass an instance into * class and pass an instance into
* {@link android.telephony.MbmsDownloadManager#download( * {@link MbmsDownloadSession#download(DownloadRequest)}
* DownloadRequest, DownloadStateCallback, Handler)}
* *
* This is optionally specified when requesting a download and will only be called while the app * This is optionally specified when requesting a download and will only be called while the app
* is running. * is running.
@@ -59,7 +57,7 @@ public class DownloadStateCallback {
* may not have been able to get a list of them in advance. * may not have been able to get a list of them in advance.
* @param state The current state of the download. * @param state The current state of the download.
*/ */
public void onStateChanged(DownloadRequest request, FileInfo fileInfo, public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
@MbmsDownloadManager.DownloadStatus int state) { @MbmsDownloadSession.DownloadStatus int state) {
} }
} }

View File

@@ -59,7 +59,7 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable {
FileServiceInfo(Parcel in) { FileServiceInfo(Parcel in) {
super(in); super(in);
files = new ArrayList<FileInfo>(); files = new ArrayList<FileInfo>();
in.readList(files, null); in.readList(files, FileInfo.class.getClassLoader());
} }
@Override @Override

View File

@@ -29,8 +29,9 @@ interface IDownloadStateCallback
* Gives progress callbacks for a given DownloadRequest. Includes a FileInfo * Gives progress callbacks for a given DownloadRequest. Includes a FileInfo
* as the list of files may not have been known at request-time. * as the list of files may not have been known at request-time.
*/ */
void progress(in DownloadRequest request, in FileInfo fileInfo, int currentDownloadSize, void onProgressUpdated(in DownloadRequest request, in FileInfo fileInfo,
int fullDownloadSize, int currentDecodedSize, int fullDecodedSize); 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);
} }

View File

@@ -24,11 +24,11 @@ import java.util.List;
* The interface the clients top-level file download listener will satisfy. * The interface the clients top-level file download listener will satisfy.
* @hide * @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();
} }

View File

@@ -24,11 +24,11 @@ import java.util.List;
* The interface the clients top-level streaming listener will satisfy. * The interface the clients top-level streaming listener will satisfy.
* @hide * @hide
*/ */
oneway interface IMbmsStreamingManagerCallback oneway interface IMbmsStreamingSessionCallback
{ {
void error(int errorCode, String message); void onError(int errorCode, String message);
void streamingServicesUpdated(in List<StreamingServiceInfo> services); void onStreamingServicesUpdated(in List<StreamingServiceInfo> services);
void middlewareReady(); void onMiddlewareReady();
} }

View File

@@ -20,9 +20,9 @@ package android.telephony.mbms;
* @hide * @hide
*/ */
oneway interface IStreamingServiceCallback { oneway interface IStreamingServiceCallback {
void error(int errorCode, String message); void onError(int errorCode, String message);
void streamStateUpdated(int state, int reason); void onStreamStateUpdated(int state, int reason);
void mediaDescriptionUpdated(); void onMediaDescriptionUpdated();
void broadcastSignalStrengthUpdated(int signalStrength); void onBroadcastSignalStrengthUpdated(int signalStrength);
void streamMethodUpdated(int methodType); void onStreamMethodUpdated(int methodType);
} }

View File

@@ -22,19 +22,24 @@ import android.os.RemoteException;
import java.util.List; import java.util.List;
/** @hide */ /** @hide */
public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub { public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
private final Handler mHandler; 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) { Handler handler) {
mAppCallback = appCallback; mAppCallback = appCallback;
mHandler = handler; mHandler = handler;
} }
@Override @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() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -44,7 +49,11 @@ public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallbac
} }
@Override @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() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -54,7 +63,11 @@ public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallbac
} }
@Override @Override
public void middlewareReady() throws RemoteException { public void onMiddlewareReady() throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -66,4 +79,8 @@ public class InternalDownloadManagerCallback extends IMbmsDownloadManagerCallbac
public Handler getHandler() { public Handler getHandler() {
return mHandler; return mHandler;
} }
public void stop() {
mIsStopped = true;
}
} }

View File

@@ -25,6 +25,7 @@ import android.os.RemoteException;
public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub { public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
private final Handler mHandler; private final Handler mHandler;
private final DownloadStateCallback mAppCallback; private final DownloadStateCallback mAppCallback;
private volatile boolean mIsStopped = false;
public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) { public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
mAppCallback = appCallback; mAppCallback = appCallback;
@@ -32,9 +33,13 @@ public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
} }
@Override @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 currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
final int fullDecodedSize) throws RemoteException { final int fullDecodedSize) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -45,13 +50,21 @@ public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
} }
@Override @Override
public void state(final DownloadRequest request, final FileInfo fileInfo, final int state) public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
throws RemoteException { final int state) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
mAppCallback.onStateChanged(request, fileInfo, state); mAppCallback.onStateUpdated(request, fileInfo, state);
} }
}); });
} }
public void stop() {
mIsStopped = true;
}
} }

View File

@@ -23,6 +23,7 @@ import android.os.RemoteException;
public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub { public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
private final StreamingServiceCallback mAppCallback; private final StreamingServiceCallback mAppCallback;
private final Handler mHandler; private final Handler mHandler;
private volatile boolean mIsStopped = false;
public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) { public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
mAppCallback = appCallback; mAppCallback = appCallback;
@@ -30,7 +31,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
@Override @Override
public void error(int errorCode, String message) throws RemoteException { public void onError(final int errorCode, final String message) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -40,7 +45,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
@Override @Override
public void streamStateUpdated(int state, int reason) throws RemoteException { public void onStreamStateUpdated(final int state, final int reason) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -50,7 +59,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
@Override @Override
public void mediaDescriptionUpdated() throws RemoteException { public void onMediaDescriptionUpdated() throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -60,7 +73,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
@Override @Override
public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException { public void onBroadcastSignalStrengthUpdated(final int signalStrength) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -70,7 +87,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
@Override @Override
public void streamMethodUpdated(int methodType) throws RemoteException { public void onStreamMethodUpdated(final int methodType) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -78,4 +99,8 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
} }
}); });
} }
public void stop() {
mIsStopped = true;
}
} }

View File

@@ -18,25 +18,27 @@ package android.telephony.mbms;
import android.os.Handler; import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.IMbmsStreamingManagerCallback;
import android.telephony.mbms.MbmsStreamingManagerCallback;
import android.telephony.mbms.StreamingServiceInfo;
import java.util.List; import java.util.List;
/** @hide */ /** @hide */
public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub { public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
private final Handler mHandler; private final Handler mHandler;
private final MbmsStreamingManagerCallback mAppCallback; private final MbmsStreamingSessionCallback mAppCallback;
private volatile boolean mIsStopped = false;
public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback, public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
Handler handler) { Handler handler) {
mAppCallback = appCallback; mAppCallback = appCallback;
mHandler = handler; mHandler = handler;
} }
@Override @Override
public void error(int errorCode, String message) throws RemoteException { public void onError(final int errorCode, final String message) throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -46,8 +48,12 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
} }
@Override @Override
public void streamingServicesUpdated(List<StreamingServiceInfo> services) public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services)
throws RemoteException { throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -57,7 +63,11 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
} }
@Override @Override
public void middlewareReady() throws RemoteException { public void onMiddlewareReady() throws RemoteException {
if (mIsStopped) {
return;
}
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -69,4 +79,8 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
public Handler getHandler() { public Handler getHandler() {
return mHandler; return mHandler;
} }
public void stop() {
mIsStopped = true;
}
} }

View File

@@ -25,7 +25,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.telephony.MbmsDownloadManager; import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.vendor.VendorUtils; import android.telephony.mbms.vendor.VendorUtils;
import android.util.Log; import android.util.Log;
@@ -36,8 +36,10 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@@ -115,8 +117,19 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
//@SystemApi //@SystemApi
public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5; public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5;
/**
* Indicates that the manager was unable to notify the app of the completed download.
* This is a fatal result code and no result extras should be expected.
* @hide
*/
@SystemApi
public static final int RESULT_APP_NOTIFICATION_ERROR = 6;
private static final String LOG_TAG = "MbmsDownloadReceiver"; private static final String LOG_TAG = "MbmsDownloadReceiver";
private static final String TEMP_FILE_SUFFIX = ".embms.temp"; private static final String TEMP_FILE_SUFFIX = ".embms.temp";
private static final String TEMP_FILE_STAGING_LOCATION = "staged_completed_files";
private static final int MAX_TEMP_FILE_RETRIES = 5; private static final int MAX_TEMP_FILE_RETRIES = 5;
private String mFileProviderAuthorityCache = null; private String mFileProviderAuthorityCache = null;
@@ -149,11 +162,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
private boolean verifyIntentContents(Context context, Intent intent) { private boolean verifyIntentContents(Context context, Intent intent) {
if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { 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."); Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false; return false;
} }
if (!intent.hasExtra(VendorUtils.EXTRA_REQUEST)) { if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring."); Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false; return false;
} }
@@ -161,7 +174,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring."); Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false; 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. " + Log.w(LOG_TAG, "Download result did not include the associated file info. " +
"Ignoring."); "Ignoring.");
return false; return false;
@@ -171,7 +184,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
"temp file. Ignoring."); "temp file. Ignoring.");
return false; return false;
} }
DownloadRequest request = intent.getParcelableExtra(VendorUtils.EXTRA_REQUEST); DownloadRequest request = intent.getParcelableExtra(
MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX; String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
File expectedTokenFile = new File( File expectedTokenFile = new File(
MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()), MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
@@ -211,20 +225,25 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
} }
private void moveDownloadedFile(Context context, Intent intent) { private void moveDownloadedFile(Context context, Intent intent) {
DownloadRequest request = intent.getParcelableExtra(VendorUtils.EXTRA_REQUEST); DownloadRequest request = intent.getParcelableExtra(
MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
Intent intentForApp = request.getIntentForApp(); Intent intentForApp = request.getIntentForApp();
if (intentForApp == null) {
Log.i(LOG_TAG, "Malformed app notification intent");
setResultCode(RESULT_APP_NOTIFICATION_ERROR);
return;
}
int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT, int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
MbmsDownloadManager.RESULT_CANCELLED); MbmsDownloadSession.RESULT_CANCELLED);
intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_DOWNLOAD_RESULT, result); 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."); Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
context.sendBroadcast(intentForApp); context.sendBroadcast(intentForApp);
return; return;
} }
Uri destinationUri = request.getDestinationUri();
Uri finalTempFile = intent.getParcelableExtra(VendorUtils.EXTRA_FINAL_URI); Uri finalTempFile = intent.getParcelableExtra(VendorUtils.EXTRA_FINAL_URI);
if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) { if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile); Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
@@ -233,24 +252,31 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
} }
FileInfo completedFileInfo = FileInfo completedFileInfo =
(FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO); (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo); Path stagingDirectory = FileSystems.getDefault().getPath(
MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
TEMP_FILE_STAGING_LOCATION);
Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath); Uri stagedFileLocation;
if (finalFileLocation == null) { try {
stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
} catch (IOException e) {
Log.w(LOG_TAG, "Failed to move temp file to final destination"); Log.w(LOG_TAG, "Failed to move temp file to final destination");
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR); setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
return; return;
} }
intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_COMPLETED_FILE_URI, finalFileLocation); intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
intentForApp.putExtra(MbmsDownloadManager.EXTRA_MBMS_FILE_INFO, completedFileInfo); stagedFileLocation);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
context.sendBroadcast(intentForApp); context.sendBroadcast(intentForApp);
setResultCode(RESULT_OK); setResultCode(RESULT_OK);
} }
private void cleanupPostMove(Context context, Intent intent) { private void cleanupPostMove(Context context, Intent intent) {
DownloadRequest request = intent.getParcelableExtra(VendorUtils.EXTRA_REQUEST); DownloadRequest request = intent.getParcelableExtra(
MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
if (request == null) { if (request == null) {
Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring."); Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
return; return;
@@ -404,63 +430,22 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
} }
} }
private static String calculateDestinationFileRelativePath(DownloadRequest request,
FileInfo info) {
List<String> filePathComponents = info.getUri().getPathSegments();
List<String> requestPathComponents = request.getSourceUri().getPathSegments();
Iterator<String> filePathIter = filePathComponents.iterator();
Iterator<String> requestPathIter = requestPathComponents.iterator();
StringBuilder pathBuilder = new StringBuilder();
// Iterate through the segments of the carrier's URI to the file, along with the segments
// of the source URI specified in the download request. The relative path is calculated
// as the tail of the file's URI that does not match the path segments in the source URI.
while (filePathIter.hasNext()) {
String currFilePathComponent = filePathIter.next();
if (requestPathIter.hasNext()) {
String requestFilePathComponent = requestPathIter.next();
if (requestFilePathComponent.equals(currFilePathComponent)) {
continue;
}
}
pathBuilder.append(currFilePathComponent);
pathBuilder.append('/');
}
// remove the trailing slash
if (pathBuilder.length() > 0) {
pathBuilder.deleteCharAt(pathBuilder.length() - 1);
}
return pathBuilder.toString();
}
/* /*
* Moves a tempfile located at fromPath to a new location at toPath. If * Moves a tempfile located at fromPath to a new location in the staging directory.
* toPath is a directory, the destination file will be located at relativePath
* underneath toPath.
*/ */
private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) { private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) { if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme"); Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
return null; return null;
} }
if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
return null;
}
File fromFile = new File(fromPath.getSchemeSpecificPart()); Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
File toFile = new File(toPath.getSchemeSpecificPart()); if (!Files.isDirectory(stagingDirectory)) {
if (toFile.isDirectory()) { Files.createDirectory(stagingDirectory);
toFile = new File(toFile, relativePath);
} }
toFile.getParentFile().mkdirs(); Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
if (fromFile.renameTo(toFile)) { return Uri.fromFile(result.toFile());
return Uri.fromFile(toFile);
} else if (manualMove(fromFile, toFile)) {
return Uri.fromFile(toFile);
}
return null;
} }
private static boolean verifyTempFilePath(Context context, String serviceId, private static boolean verifyTempFilePath(Context context, String serviceId,
@@ -512,7 +497,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
private String getMiddlewarePackageCached(Context context) { private String getMiddlewarePackageCached(Context context) {
if (mMiddlewarePackageNameCache == null) { if (mMiddlewarePackageNameCache == null) {
mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context, mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName; MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
} }
return mMiddlewarePackageNameCache; return mMiddlewarePackageNameCache;
} }

View File

@@ -16,7 +16,7 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.telephony.MbmsDownloadManager; import android.telephony.MbmsDownloadSession;
import java.util.List; import java.util.List;
@@ -25,11 +25,11 @@ import java.util.List;
* cell-broadcast. * cell-broadcast.
* @hide * @hide
*/ */
public class MbmsDownloadManagerCallback { public class MbmsDownloadSessionCallback {
/** /**
* Indicates that the middleware has encountered an asynchronous error. * Indicates that the middleware has encountered an asynchronous error.
* @param errorCode Any error code listed in {@link MbmsException} * @param errorCode Any error code listed in {@link MbmsErrors}
* @param message A message, intended for debugging purposes, describing the error in further * @param message A message, intended for debugging purposes, describing the error in further
* detail. * detail.
*/ */
@@ -42,8 +42,9 @@ public class MbmsDownloadManagerCallback {
* *
* This will only be called after the application has requested a list of file services and * This will only be called after the application has requested a list of file services and
* specified a service class list of interest via * specified a service class list of interest via
* {@link MbmsDownloadManager#getFileServices(List)}. If there are subsequent calls to * {@link MbmsDownloadSession#requestUpdateFileServices(List)}. If there are subsequent calls to
* {@link MbmsDownloadManager#getFileServices(List)}, this method may not be called again if * {@link MbmsDownloadSession#requestUpdateFileServices(List)},
* this method may not be called again if
* the list of service classes would remain the same. * the list of service classes would remain the same.
* *
* @param services The most recently updated list of available file services. * @param services The most recently updated list of available file services.
@@ -56,9 +57,9 @@ public class MbmsDownloadManagerCallback {
* Called to indicate that the middleware has been initialized and is ready. * Called to indicate that the middleware has been initialized and is ready.
* *
* Before this method is called, calling any method on an instance of * 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 IllegalStateException}
* being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} * being thrown or {@link #onError(int, String)} being called with error code
* or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
*/ */
public void onMiddlewareReady() { public void onMiddlewareReady() {
// default implementation empty // default implementation empty

View File

@@ -16,8 +16,10 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.telephony.MbmsStreamingSession;
/** @hide */ /** @hide */
public class MbmsException extends Exception { public class MbmsErrors {
/** Indicates that the operation was successful. */ /** Indicates that the operation was successful. */
public static final int SUCCESS = 0; public static final int SUCCESS = 0;
@@ -31,8 +33,8 @@ public class MbmsException extends Exception {
/** /**
* Indicates that the app attempted to perform an operation on an instance of * 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 android.telephony.MbmsStreamingManager} without being bound to the middleware. * {@link MbmsStreamingSession} without being bound to the middleware.
*/ */
public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
@@ -47,8 +49,7 @@ public class MbmsException extends Exception {
private InitializationErrors() {} private InitializationErrors() {}
/** /**
* Indicates that the app tried to create more than one instance each of * Indicates that the app tried to create more than one instance each of
* {@link android.telephony.MbmsStreamingManager} or * {@link MbmsStreamingSession} or {@link android.telephony.MbmsDownloadSession}.
* TODO link android.telephony.MbmsDownloadManager
*/ */
public static final int ERROR_DUPLICATE_INITIALIZE = 101; public static final int ERROR_DUPLICATE_INITIALIZE = 101;
/** Indicates that the app is not authorized to access media via MBMS.*/ /** Indicates that the app is not authorized to access media via MBMS.*/
@@ -65,8 +66,8 @@ public class MbmsException extends Exception {
private GeneralErrors() {} private GeneralErrors() {}
/** /**
* Indicates that the app attempted to perform an operation before receiving notification * Indicates that the app attempted to perform an operation before receiving notification
* that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()} * 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; public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
/** /**
@@ -108,7 +109,7 @@ public class MbmsException extends Exception {
/** /**
* Indicates that the app called * Indicates that the app called
* {@link android.telephony.MbmsStreamingManager#startStreaming( * {@link MbmsStreamingSession#startStreaming(
* StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)} * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
* more than once for the same {@link StreamingServiceInfo}. * more than once for the same {@link StreamingServiceInfo}.
*/ */
@@ -130,15 +131,5 @@ public class MbmsException extends Exception {
public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
} }
private final int mErrorCode; private MbmsErrors() {}
/** @hide */
public MbmsException(int errorCode) {
super();
mErrorCode = errorCode;
}
public int getErrorCode() {
return mErrorCode;
}
} }

View File

@@ -16,26 +16,27 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.os.RemoteException; import android.os.Handler;
import android.telephony.MbmsStreamingManager; import android.telephony.MbmsStreamingSession;
import java.util.List; import java.util.List;
/** /**
* A callback class that is used to receive information from the middleware on MBMS streaming * A callback class that is used to receive information from the middleware on MBMS streaming
* services. An instance of this object should be passed into * services. An instance of this object should be passed into
* {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}. * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
* @hide * @hide
*/ */
public class MbmsStreamingManagerCallback { public class MbmsStreamingSessionCallback {
/** /**
* Called by the middleware when it has detected an error condition. The possible error codes * Called by the middleware when it has detected an error condition. The possible error codes
* are listed in {@link MbmsException}. * are listed in {@link MbmsErrors}.
* @param errorCode The error code. * @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes. * @param message A human-readable message generated by the middleware for debugging purposes.
*/ */
public void onError(int errorCode, String message) { public void onError(int errorCode, @Nullable String message) {
// default implementation empty // default implementation empty
} }
@@ -48,8 +49,7 @@ public class MbmsStreamingManagerCallback {
* call with the same service class list would return different * call with the same service class list would return different
* results. * results.
* *
* @param services a List of StreamingServiceInfos * @param services The list of available services.
*
*/ */
public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) { public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
// default implementation empty // default implementation empty
@@ -59,9 +59,9 @@ public class MbmsStreamingManagerCallback {
* Called to indicate that the middleware has been initialized and is ready. * Called to indicate that the middleware has been initialized and is ready.
* *
* Before this method is called, calling any method on an instance of * Before this method is called, calling any method on an instance of
* {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException} * {@link MbmsStreamingSession} will result in an {@link IllegalStateException} or an error
* being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} * delivered via {@link #onError(int, String)} with error code
* or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/ */
public void onMiddlewareReady() { public void onMiddlewareReady() {
// default implementation empty // default implementation empty

View File

@@ -27,7 +27,7 @@ import android.content.pm.ProviderInfo;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.telephony.MbmsDownloadManager; import android.telephony.MbmsDownloadSession;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -181,7 +181,7 @@ public class MbmsTempFileProvider extends ContentProvider {
return new File(storedTempFileRoot).getCanonicalFile(); return new File(storedTempFileRoot).getCanonicalFile();
} else { } else {
return new File(context.getFilesDir(), return new File(context.getFilesDir(),
MbmsDownloadManager.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile(); MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
} }
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Unable to canonicalize temp file root path " + e); throw new RuntimeException("Unable to canonicalize temp file root path " + e);

View File

@@ -68,19 +68,20 @@ public class MbmsUtils {
return downloadServices.get(0).serviceInfo; return downloadServices.get(0).serviceInfo;
} }
public static void startBinding(Context context, String serviceAction, public static int startBinding(Context context, String serviceAction,
ServiceConnection serviceConnection) throws MbmsException { ServiceConnection serviceConnection) {
Intent bindIntent = new Intent(); Intent bindIntent = new Intent();
ServiceInfo mbmsServiceInfo = ServiceInfo mbmsServiceInfo =
MbmsUtils.getMiddlewareServiceInfo(context, serviceAction); MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
if (mbmsServiceInfo == null) { if (mbmsServiceInfo == null) {
throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE); return MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE;
} }
bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo)); bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE); context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
return MbmsErrors.SUCCESS;
} }
/** /**

View File

@@ -16,6 +16,8 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
@@ -26,12 +28,13 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
* Describes a cell-broadcast service. This class should not be instantiated directly -- use * Describes a cell-broadcast service. This class should not be instantiated directly -- use
* {@link StreamingServiceInfo} or TODO link FileServiceInfo * {@link StreamingServiceInfo} or {@link FileServiceInfo}
* @hide * @hide
*/ */
public class ServiceInfo { public class ServiceInfo {
@@ -59,6 +62,13 @@ public class ServiceInfo {
if (newLocales.size() > MAP_LIMIT) { if (newLocales.size() > MAP_LIMIT) {
throw new RuntimeException("bad locales length " + newLocales.size()); 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 = new HashMap(newNames.size());
names.putAll(newNames); names.putAll(newNames);
className = newClassName; className = newClassName;
@@ -115,16 +125,25 @@ public class ServiceInfo {
} }
/** /**
* 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 java.util.NoSuchElementException} may be thrown otherwise.
* @return The {@link CharSequence} providing the name of the service in the given
* {@link Locale}
*/ */
public Map<Locale, String> getNames() { public @NonNull CharSequence getNameForLocale(@NonNull Locale locale) {
return names; if (!names.containsKey(locale)) {
throw new NoSuchElementException("Locale not supported");
}
return names.get(locale);
} }
/** /**
* The class name for this service - used to categorize and filter * The class name for this service - used to categorize and filter
*/ */
public String getClassName() { public String getServiceClassName() {
return className; return className;
} }

View File

@@ -17,8 +17,10 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.Nullable;
import android.net.Uri; import android.net.Uri;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.MbmsStreamingSession;
import android.telephony.mbms.vendor.IMbmsStreamingService; import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.Log; import android.util.Log;
@@ -27,7 +29,7 @@ import java.lang.annotation.RetentionPolicy;
/** /**
* Class used to represent a single MBMS stream. After a stream has been started with * Class used to represent a single MBMS stream. After a stream has been started with
* {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
* StreamingServiceCallback, android.os.Handler)}, * StreamingServiceCallback, android.os.Handler)},
* this class is used to hold information about the stream and control it. * this class is used to hold information about the stream and control it.
* @hide * @hide
@@ -64,7 +66,7 @@ public class StreamingService {
/** /**
* State changed due to a call to {@link #stopStreaming()} or * State changed due to a call to {@link #stopStreaming()} or
* {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
* StreamingServiceCallback, android.os.Handler)} * StreamingServiceCallback, android.os.Handler)}
*/ */
public static final int REASON_BY_USER_REQUEST = 1; public static final int REASON_BY_USER_REQUEST = 1;
@@ -102,6 +104,7 @@ public class StreamingService {
public final static int UNICAST_METHOD = 2; public final static int UNICAST_METHOD = 2;
private final int mSubscriptionId; private final int mSubscriptionId;
private final MbmsStreamingSession mParentSession;
private final StreamingServiceInfo mServiceInfo; private final StreamingServiceInfo mServiceInfo;
private final InternalStreamingServiceCallback mCallback; private final InternalStreamingServiceCallback mCallback;
@@ -112,25 +115,25 @@ public class StreamingService {
*/ */
public StreamingService(int subscriptionId, public StreamingService(int subscriptionId,
IMbmsStreamingService service, IMbmsStreamingService service,
MbmsStreamingSession session,
StreamingServiceInfo streamingServiceInfo, StreamingServiceInfo streamingServiceInfo,
InternalStreamingServiceCallback callback) { InternalStreamingServiceCallback callback) {
mSubscriptionId = subscriptionId; mSubscriptionId = subscriptionId;
mParentSession = session;
mService = service; mService = service;
mServiceInfo = streamingServiceInfo; mServiceInfo = streamingServiceInfo;
mCallback = callback; mCallback = callback;
} }
/** /**
* Retreive the Uri used to play this stream. * Retrieve the Uri used to play this stream.
* *
* This may throw a {@link MbmsException} with the error code * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
* {@link MbmsException#ERROR_MIDDLEWARE_LOST}
* *
* May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * @return The {@link Uri} to pass to the streaming client, or {@code null} if an error
* * occurred.
* @return The {@link Uri} to pass to the streaming client.
*/ */
public Uri getPlaybackUri() throws MbmsException { public @Nullable Uri getPlaybackUri() {
if (mService == null) { if (mService == null) {
throw new IllegalStateException("No streaming service attached"); throw new IllegalStateException("No streaming service attached");
} }
@@ -140,25 +143,26 @@ public class StreamingService {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); Log.w(LOG_TAG, "Remote process died");
mService = null; mService = null;
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); mParentSession.onStreamingServiceStopped(this);
sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return null;
} }
} }
/** /**
* Retreive the info for this StreamingService. * Retrieve the {@link StreamingServiceInfo} corresponding to this stream.
*/ */
public StreamingServiceInfo getInfo() { public StreamingServiceInfo getInfo() {
return mServiceInfo; return mServiceInfo;
} }
/** /**
* Stop streaming this service. * Stop streaming this service. Further operations on this object will fail with an
* This may throw a {@link MbmsException} with the error code * {@link IllegalStateException}.
* {@link MbmsException#ERROR_MIDDLEWARE_LOST}
* *
* May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*/ */
public void stopStreaming() throws MbmsException { public void stopStreaming() {
if (mService == null) { if (mService == null) {
throw new IllegalStateException("No streaming service attached"); throw new IllegalStateException("No streaming service attached");
} }
@@ -168,32 +172,22 @@ public class StreamingService {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); Log.w(LOG_TAG, "Remote process died");
mService = null; mService = null;
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} finally {
mParentSession.onStreamingServiceStopped(this);
} }
} }
/** /** @hide */
* Disposes of this stream. Further operations on this object will fail with an public InternalStreamingServiceCallback getCallback() {
* {@link IllegalStateException}. return mCallback;
* }
* This may throw a {@link MbmsException} with the error code
* {@link MbmsException#ERROR_MIDDLEWARE_LOST}
* May also throw an {@link IllegalStateException}
*/
public void dispose() throws MbmsException {
if (mService == null) {
throw new IllegalStateException("No streaming service attached");
}
private void sendErrorToApp(int errorCode, String message) {
try { try {
mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId()); mCallback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died"); // Ignore, should not happen locally.
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("StreamingService state inconsistent with middleware");
} finally {
mService = null;
} }
} }
} }

View File

@@ -16,6 +16,8 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.Nullable;
/** /**
* A callback class for use when the application is actively streaming content. The middleware * A callback class for use when the application is actively streaming content. The middleware
* will provide updates on the status of the stream via this callback. * will provide updates on the status of the stream via this callback.
@@ -34,11 +36,11 @@ public class StreamingServiceCallback {
/** /**
* Called by the middleware when it has detected an error condition in this stream. The * Called by the middleware when it has detected an error condition in this stream. The
* possible error codes are listed in {@link MbmsException}. * possible error codes are listed in {@link MbmsErrors}.
* @param errorCode The error code. * @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes. * @param message A human-readable message generated by the middleware for debugging purposes.
*/ */
public void onError(int errorCode, String message) { public void onError(int errorCode, @Nullable String message) {
// default implementation empty // default implementation empty
} }

View File

@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.net.Uri; import android.net.Uri;
import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.FileInfo; import android.telephony.mbms.FileInfo;
import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.IMbmsDownloadSessionCallback;
import android.telephony.mbms.IDownloadStateCallback; import android.telephony.mbms.IDownloadStateCallback;
/** /**
@@ -28,13 +28,18 @@ import android.telephony.mbms.IDownloadStateCallback;
*/ */
interface IMbmsDownloadService 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 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); List<DownloadRequest> listPendingDownloads(int subscriptionId);

View File

@@ -17,7 +17,7 @@
package android.telephony.mbms.vendor; package android.telephony.mbms.vendor;
import android.net.Uri; import android.net.Uri;
import android.telephony.mbms.IMbmsStreamingManagerCallback; import android.telephony.mbms.IMbmsStreamingSessionCallback;
import android.telephony.mbms.IStreamingServiceCallback; import android.telephony.mbms.IStreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo; import android.telephony.mbms.StreamingServiceInfo;
@@ -26,18 +26,16 @@ import android.telephony.mbms.StreamingServiceInfo;
*/ */
interface IMbmsStreamingService interface IMbmsStreamingService
{ {
int initialize(IMbmsStreamingManagerCallback listener, int subId); int initialize(IMbmsStreamingSessionCallback callback, int subId);
int getStreamingServices(int subId, in List<String> serviceClasses); int requestUpdateStreamingServices(int subId, in List<String> serviceClasses);
int startStreaming(int subId, String serviceId, int startStreaming(int subId, String serviceId,
IStreamingServiceCallback listener); IStreamingServiceCallback callback);
Uri getPlaybackUri(int subId, String serviceId); Uri getPlaybackUri(int subId, String serviceId);
void stopStreaming(int subId, String serviceId); void stopStreaming(int subId, String serviceId);
void disposeStream(int subId, String serviceId);
void dispose(int subId); void dispose(int subId);
} }

View File

@@ -20,17 +20,21 @@ import android.annotation.NonNull;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.content.Intent; import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.DownloadStateCallback; import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.DownloadStateCallback;
import android.telephony.mbms.FileInfo; import android.telephony.mbms.FileInfo;
import android.telephony.mbms.FileServiceInfo; import android.telephony.mbms.FileServiceInfo;
import android.telephony.mbms.IDownloadStateCallback; import android.telephony.mbms.IDownloadStateCallback;
import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.IMbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsErrors;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Base class for MbmsDownloadService. The middleware should return an instance of this object from * Base class for MbmsDownloadService. The middleware should return an instance of this object from
@@ -39,21 +43,24 @@ import java.util.List;
*/ */
//@SystemApi //@SystemApi
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { 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. * Initialize the download service for this app and subId, registering the listener.
* *
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
* will be intercepted and passed to the app as * will be intercepted and passed to the app as
* {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
* *
* May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors} * May return any value from {@link MbmsErrors.InitializationErrors}
* or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via * or {@link MbmsErrors#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 callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use. * @param subscriptionId The subscription ID to use.
*/ */
public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback) public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback)
throws RemoteException { throws RemoteException {
return 0; return 0;
} }
@@ -64,7 +71,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
*/ */
@Override @Override
public final int initialize(final int subscriptionId, public final int initialize(final int subscriptionId,
final IMbmsDownloadManagerCallback callback) throws RemoteException { final IMbmsDownloadSessionCallback callback) throws RemoteException {
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() { callback.asBinder().linkToDeath(new DeathRecipient() {
@Override @Override
@@ -73,11 +80,11 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
} }
}, 0); }, 0);
return initialize(subscriptionId, new MbmsDownloadManagerCallback() { return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
@Override @Override
public void onError(int errorCode, String message) { public void onError(int errorCode, String message) {
try { try {
callback.error(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -86,7 +93,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override @Override
public void onFileServicesUpdated(List<FileServiceInfo> services) { public void onFileServicesUpdated(List<FileServiceInfo> services) {
try { try {
callback.fileServicesUpdated(services); callback.onFileServicesUpdated(services);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -95,7 +102,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override @Override
public void onMiddlewareReady() { public void onMiddlewareReady() {
try { try {
callback.middlewareReady(); callback.onMiddlewareReady();
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -106,7 +113,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
/** /**
* Registers serviceClasses of interest with the appName/subId key. * Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported * 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 * Note that subsequent calls with the same uid and subId will replace
* the service class list. * the service class list.
@@ -117,11 +124,11 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* @param serviceClasses The service classes that the app wishes to get info on. The strings * @param serviceClasses The service classes that the app wishes to get info on. The strings
* may contain arbitrary data as negotiated between the app and the * may contain arbitrary data as negotiated between the app and the
* carrier. * carrier.
* @return One of {@link MbmsException#SUCCESS} or * @return One of {@link MbmsErrors#SUCCESS} or
* {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}, * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
*/ */
@Override @Override
public int getFileServices(int subscriptionId, List<String> serviceClasses) public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses)
throws RemoteException { throws RemoteException {
return 0; return 0;
} }
@@ -133,13 +140,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* *
* If the calling app (as identified by the calling UID) currently has any pending download * If the calling app (as identified by the calling UID) currently has any pending download
* requests that have not been canceled, the middleware must return * requests that have not been canceled, the middleware must return
* {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here. * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
* *
* @param subscriptionId The subscription id the download is operating under. * @param subscriptionId The subscription id the download is operating under.
* @param rootDirectoryPath The path to the app's temp file root directory. * @param rootDirectoryPath The path to the app's temp file root directory.
* @return {@link MbmsException#SUCCESS}, * @return {@link MbmsErrors#SUCCESS},
* {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
* {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
*/ */
@Override @Override
public int setTempFileRootDirectory(int subscriptionId, public int setTempFileRootDirectory(int subscriptionId,
@@ -155,12 +162,32 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* this is not the case, an {@link IllegalStateException} may be thrown. * this is not the case, an {@link IllegalStateException} may be thrown.
* *
* @param downloadRequest An object describing the set of files to be downloaded. * @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 * @return Any error from {@link MbmsErrors.GeneralErrors}
* the app while both are still running. * or {@link MbmsErrors#SUCCESS}
* @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 MbmsErrors.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; return 0;
} }
@@ -169,36 +196,101 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* @hide * @hide
*/ */
@Override @Override
public final int download(DownloadRequest downloadRequest, IDownloadStateCallback callback) public final int registerStateCallback(
final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
throws RemoteException { throws RemoteException {
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() { DeathRecipient deathRecipient = new DeathRecipient() {
@Override @Override
public void binderDied() { public void binderDied() {
onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); 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 @Override
public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
fullDecodedSize) { fullDecodedSize) {
try { try {
callback.progress(request, fileInfo, currentDownloadSize, fullDownloadSize, callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
fullDownloadSize,
currentDecodedSize, fullDecodedSize); currentDecodedSize, fullDecodedSize);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); 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 MbmsErrors.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 * 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 * 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)}. * {@link #cancelDownload(DownloadRequest)}.
* The middleware must return a non-null result synchronously or throw an exception * The middleware must return a non-null result synchronously or throw an exception
* inheriting from {@link RuntimeException}. * inheriting from {@link RuntimeException}.
@@ -214,13 +306,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* Issues a request to cancel the specified download request. * Issues a request to cancel the specified download request.
* *
* If the middleware is unable to cancel the request for whatever reason, it should return * If the middleware is unable to cancel the request for whatever reason, it should return
* synchronously with an error. If this method returns {@link MbmsException#SUCCESS}, the app * synchronously with an error. If this method returns {@link MbmsErrors#SUCCESS}, the app
* will no longer be expecting any more file-completed intents from the middleware for this * will no longer be expecting any more file-completed intents from the middleware for this
* {@link DownloadRequest}. * {@link DownloadRequest}.
* @param downloadRequest The request to cancel * @param downloadRequest The request to cancel
* @return {@link MbmsException#SUCCESS}, * @return {@link MbmsErrors#SUCCESS},
* {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}, * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
* {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
*/ */
@Override @Override
public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException { public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
@@ -232,7 +324,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* *
* If the middleware has not yet been properly initialized or if it has no records of the * 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}, * 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 downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on. * @param fileInfo The particular file within the request to get information on.
@@ -252,7 +344,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* In addition, current in-progress downloads must not be interrupted. * In addition, current in-progress downloads must not be interrupted.
* *
* If the middleware is not aware of the specified download request, return * If the middleware is not aware of the specified download request, return
* {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
* *
* @param downloadRequest The request to re-download files for. * @param downloadRequest The request to re-download files for.
*/ */
@@ -266,7 +358,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* Signals that the app wishes to dispose of the session identified by the * 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 * {@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 * 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. * after this method has been called by the app.
* *
* Any download requests issued by the app should remain in effect until the app calls * Any download requests issued by the app should remain in effect until the app calls

View File

@@ -22,10 +22,10 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Binder; import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.IMbmsStreamingManagerCallback; import android.telephony.mbms.IMbmsStreamingSessionCallback;
import android.telephony.mbms.IStreamingServiceCallback; import android.telephony.mbms.IStreamingServiceCallback;
import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsStreamingManagerCallback; import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.StreamingService; import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback; import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo; import android.telephony.mbms.StreamingServiceInfo;
@@ -44,16 +44,16 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* *
* May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
* will be intercepted and passed to the app as * will be intercepted and passed to the app as
* {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
* *
* May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors} * May return any value from {@link MbmsErrors.InitializationErrors}
* or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
* {@link IMbmsStreamingManagerCallback#error(int, String)}. * {@link IMbmsStreamingSessionCallback#onError(int, String)}.
* *
* @param callback The callback to use to communicate with the app. * @param callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use. * @param subscriptionId The subscription ID to use.
*/ */
public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId) public int initialize(MbmsStreamingSessionCallback callback, int subscriptionId)
throws RemoteException { throws RemoteException {
return 0; return 0;
} }
@@ -63,7 +63,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @hide * @hide
*/ */
@Override @Override
public final int initialize(final IMbmsStreamingManagerCallback callback, public final int initialize(final IMbmsStreamingSessionCallback callback,
final int subscriptionId) throws RemoteException { final int subscriptionId) throws RemoteException {
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() { callback.asBinder().linkToDeath(new DeathRecipient() {
@@ -73,20 +73,20 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
} }
}, 0); }, 0);
return initialize(new MbmsStreamingManagerCallback() { return initialize(new MbmsStreamingSessionCallback() {
@Override @Override
public void onError(int errorCode, String message) { public void onError(final int errorCode, final String message) {
try { try {
callback.error(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
} }
@Override @Override
public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) { public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
try { try {
callback.streamingServicesUpdated(services); callback.onStreamingServicesUpdated(services);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -95,7 +95,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override @Override
public void onMiddlewareReady() { public void onMiddlewareReady() {
try { try {
callback.middlewareReady(); callback.onMiddlewareReady();
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -107,7 +107,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/** /**
* Registers serviceClasses of interest with the appName/subId key. * Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported * Starts async fetching data on streaming services of matching classes to be reported
* later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)} * later via {@link IMbmsStreamingSessionCallback#onStreamingServicesUpdated(List)}
* *
* Note that subsequent calls with the same uid and subId will replace * Note that subsequent calls with the same uid and subId will replace
* the service class list. * the service class list.
@@ -118,11 +118,11 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @param serviceClasses The service classes that the app wishes to get info on. The strings * @param serviceClasses The service classes that the app wishes to get info on. The strings
* may contain arbitrary data as negotiated between the app and the * may contain arbitrary data as negotiated between the app and the
* carrier. * carrier.
* @return {@link MbmsException#SUCCESS} or any of the errors in * @return {@link MbmsErrors#SUCCESS} or any of the errors in
* {@link android.telephony.mbms.MbmsException.GeneralErrors} * {@link MbmsErrors.GeneralErrors}
*/ */
@Override @Override
public int getStreamingServices(int subscriptionId, public int requestUpdateStreamingServices(int subscriptionId,
List<String> serviceClasses) throws RemoteException { List<String> serviceClasses) throws RemoteException {
return 0; return 0;
} }
@@ -130,14 +130,14 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/** /**
* Starts streaming on a particular service. This method may perform asynchronous work. When * Starts streaming on a particular service. This method may perform asynchronous work. When
* the middleware is ready to send bits to the frontend, it should inform the app via * the middleware is ready to send bits to the frontend, it should inform the app via
* {@link IStreamingServiceCallback#streamStateUpdated(int, int)}. * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
* *
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
* *
* @param subscriptionId The subscription id to use. * @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app has requested. * @param serviceId The ID of the streaming service that the app has requested.
* @param callback The callback object on which the app wishes to receive updates. * @param callback The callback object on which the app wishes to receive updates.
* @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors} * @return Any error in {@link MbmsErrors.GeneralErrors}
*/ */
public int startStreaming(int subscriptionId, String serviceId, public int startStreaming(int subscriptionId, String serviceId,
StreamingServiceCallback callback) throws RemoteException { StreamingServiceCallback callback) throws RemoteException {
@@ -150,8 +150,8 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @hide * @hide
*/ */
@Override @Override
public int startStreaming(int subscriptionId, String serviceId, public int startStreaming(final int subscriptionId, String serviceId,
IStreamingServiceCallback callback) throws RemoteException { final IStreamingServiceCallback callback) throws RemoteException {
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() { callback.asBinder().linkToDeath(new DeathRecipient() {
@Override @Override
@@ -162,19 +162,19 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() { return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
@Override @Override
public void onError(int errorCode, String message) { public void onError(final int errorCode, final String message) {
try { try {
callback.error(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
} }
@Override @Override
public void onStreamStateUpdated(@StreamingService.StreamingState int state, public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
@StreamingService.StreamingStateChangeReason int reason) { @StreamingService.StreamingStateChangeReason final int reason) {
try { try {
callback.streamStateUpdated(state, reason); callback.onStreamStateUpdated(state, reason);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -183,25 +183,25 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override @Override
public void onMediaDescriptionUpdated() { public void onMediaDescriptionUpdated() {
try { try {
callback.mediaDescriptionUpdated(); callback.onMediaDescriptionUpdated();
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
} }
@Override @Override
public void onBroadcastSignalStrengthUpdated(int signalStrength) { public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
try { try {
callback.broadcastSignalStrengthUpdated(signalStrength); callback.onBroadcastSignalStrengthUpdated(signalStrength);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
} }
@Override @Override
public void onStreamMethodUpdated(int methodType) { public void onStreamMethodUpdated(final int methodType) {
try { try {
callback.streamMethodUpdated(methodType); callback.onStreamMethodUpdated(methodType);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
} }
@@ -228,7 +228,11 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/** /**
* Stop streaming the stream identified by {@code serviceId}. Notification of the resulting * Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
* stream state change should be reported to the app via * stream state change should be reported to the app via
* {@link IStreamingServiceCallback#streamStateUpdated(int, int)}. * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
*
* In addition, the callback provided via
* {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
* used after this method has called by the app.
* *
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
* *
@@ -240,28 +244,11 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
throws RemoteException { throws RemoteException {
} }
/**
* Dispose of the stream identified by {@code serviceId} for the app identified by the
* {@code appName} and {@code subscriptionId} arguments along with the caller's uid.
* No notification back to the app is required for this operation, and the callback provided via
* {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
* used after this method has called by the app.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app wishes to dispose of.
*/
@Override
public void disposeStream(int subscriptionId, String serviceId)
throws RemoteException {
}
/** /**
* Signals that the app wishes to dispose of the session identified by the * 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 * {@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 * app is required for this operation, and the corresponding callback provided via
* {@link #initialize(IMbmsStreamingManagerCallback, int)} should no longer be used * {@link #initialize(IMbmsStreamingSessionCallback, int)} should no longer be used
* after this method has been called by the app. * after this method has been called by the app.
* *
* May throw an {@link IllegalStateException} * May throw an {@link IllegalStateException}

View File

@@ -22,7 +22,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.telephony.mbms.DownloadRequest; import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsDownloadReceiver;
import java.io.File; import java.io.File;
@@ -39,9 +39,9 @@ public class VendorUtils {
/** /**
* The MBMS middleware should send this when a download of single file has completed or * The MBMS middleware should send this when a download of single file has completed or
* failed. Mandatory extras are * failed. Mandatory extras are
* {@link android.telephony.MbmsDownloadManager#EXTRA_MBMS_DOWNLOAD_RESULT} * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
* {@link android.telephony.MbmsDownloadManager#EXTRA_MBMS_FILE_INFO} * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
* {@link #EXTRA_REQUEST} * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
* {@link #EXTRA_TEMP_LIST} * {@link #EXTRA_TEMP_LIST}
* {@link #EXTRA_FINAL_URI} * {@link #EXTRA_FINAL_URI}
*/ */
@@ -120,12 +120,6 @@ public class VendorUtils {
public static final String EXTRA_TEMP_FILES_IN_USE = public static final String EXTRA_TEMP_FILES_IN_USE =
"android.telephony.mbms.extra.TEMP_FILES_IN_USE"; "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
/**
* Extra containing the {@link DownloadRequest} for which the download result or file
* descriptor request is for. Must not be null.
*/
public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
/** /**
* Extra containing a single {@link Uri} indicating the path to the temp file in which the * Extra containing a single {@link Uri} indicating the path to the temp file in which the
* decoded downloaded file resides. Must not be null. * decoded downloaded file resides. Must not be null.