diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java index 4c3f7e7ab5a8d..1e8cf185d4e4c 100644 --- a/telephony/java/android/telephony/MbmsDownloadManager.java +++ b/telephony/java/android/telephony/MbmsDownloadManager.java @@ -18,19 +18,19 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import android.telephony.mbms.DownloadProgressListener; import android.telephony.mbms.FileInfo; import android.telephony.mbms.DownloadRequest; -import android.telephony.mbms.IDownloadProgressListener; -import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsException; @@ -44,6 +44,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -52,147 +53,38 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; public class MbmsDownloadManager { private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName(); + /** @hide */ + // TODO: systemapi + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload"; - /** - * The MBMS middleware should send this when a download of single file has completed or - * failed. Mandatory extras are - * {@link #EXTRA_RESULT} - * {@link #EXTRA_FILE_INFO} - * {@link #EXTRA_REQUEST} - * {@link #EXTRA_TEMP_LIST} - * {@link #EXTRA_FINAL_URI} - * - * TODO: future systemapi - */ - public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = - "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL"; - - /** - * The MBMS middleware should send this when it wishes to request {@code content://} URIs to - * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory - * extras are - * {@link #EXTRA_REQUEST} - * - * Optional extras are - * {@link #EXTRA_FD_COUNT} (0 if not present) - * {@link #EXTRA_PAUSED_LIST} (empty if not present) - * - * TODO: future systemapi - */ - public static final String ACTION_FILE_DESCRIPTOR_REQUEST = - "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST"; - - /** - * The MBMS middleware should send this when it wishes to clean up temp files in the app's - * filesystem. Mandatory extras are: - * {@link #EXTRA_TEMP_FILES_IN_USE} - * - * TODO: future systemapi - */ - public static final String ACTION_CLEANUP = - "android.telephony.mbms.action.CLEANUP"; /** * Integer extra indicating the result code of the download. One of * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}. - * TODO: Not systemapi. */ public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT"; /** * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result * is for. Must not be null. - * TODO: Not systemapi. */ public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO"; - /** - * Extra containing the {@link DownloadRequest} for which the download result or file - * descriptor request is for. Must not be null. - * TODO: future systemapi (here and and all extras) except the three for the app intent - */ - public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST"; - - /** - * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this - * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp - * files will be deleted upon receipt of the intent. - * May be null. - */ - public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST"; - - /** - * Extra containing a single {@link Uri} indicating the path to the temp file in which the - * decoded downloaded file resides. Must not be null. - */ - public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI"; - - /** - * Extra containing an integer indicating the number of temp files requested. - */ - public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT"; - - /** - * Extra containing a list of {@link Uri}s that the middleware is requesting access to via - * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s - * should have scheme {@code file://}. - */ - public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST"; - - /** - * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the - * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant - * to be used for new file downloads. - */ - public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST"; - - /** - * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the - * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These - * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide - * access to previously paused downloads. - */ - public static final String EXTRA_PAUSED_URI_LIST = - "android.telephony.mbms.extra.PAUSED_URI_LIST"; - - /** - * Extra containing a string that points to the middleware's knowledge of where the temp file - * root for the app is. The path should be a canonical path as returned by - * {@link File#getCanonicalPath()} - */ - public static final String EXTRA_TEMP_FILE_ROOT = - "android.telephony.mbms.extra.TEMP_FILE_ROOT"; - - /** - * Extra containing a list of {@link Uri}s indicating temp files which the middleware is - * still using. - */ - public static final String EXTRA_TEMP_FILES_IN_USE = - "android.telephony.mbms.extra.TEMP_FILES_IN_USE"; - - /** - * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by - * file-descriptor requests and cleanup requests to specify which service they want to - * request temp files or clean up temp files for, respectively. - */ - public static final String EXTRA_SERVICE_INFO = - "android.telephony.mbms.extra.SERVICE_INFO"; - /** * Extra containing a single {@link Uri} indicating the location of the successfully * downloaded file. Set on the intent provided via * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}. * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to * {@link #RESULT_SUCCESSFUL}. - * TODO: Not systemapi. */ public static final String EXTRA_COMPLETED_FILE_URI = "android.telephony.mbms.extra.COMPLETED_FILE_URI"; public static final int RESULT_SUCCESSFUL = 1; - public static final int RESULT_CANCELLED = 2; - public static final int RESULT_EXPIRED = 3; + public static final int RESULT_CANCELLED = 2; + public static final int RESULT_EXPIRED = 3; + public static final int RESULT_IO_ERROR = 4; // TODO - more results! /** @hide */ @@ -207,8 +99,16 @@ public class MbmsDownloadManager { public static final int STATUS_PENDING_REPAIR = 3; public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4; + private static AtomicBoolean sIsInitialized = new AtomicBoolean(false); + private final Context mContext; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification"); + } + }; private AtomicReference mService = new AtomicReference<>(null); private final MbmsDownloadManagerCallback mCallback; @@ -236,10 +136,21 @@ public class MbmsDownloadManager { * * Note that this call will bind a remote service and that may take a bit. The instance of * {@link MbmsDownloadManager} that is returned will not be ready for use until - * {@link IMbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback. + * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback. * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown. * - * This also may throw an {@link IllegalArgumentException} or a {@link MbmsException}. + * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}. + * + * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this + * method while there is an active instance of {@link MbmsDownloadManager} in your process + * (in other words, one that has not had {@link #dispose()} called on it), this method will + * throw an {@link MbmsException}. If you call this method in a different process + * running under the same UID, an error will be indicated via + * {@link MbmsDownloadManagerCallback#error(int, String)}. + * + * Note that initialization may fail asynchronously. If you wish to try again after you + * receive such an asynchronous error, you must call dispose() on the instance of + * {@link MbmsDownloadManager} that you received before calling this method again. * * @param context The instance of {@link Context} to use * @param listener A callback to get asynchronous error messages and file service updates. @@ -249,8 +160,16 @@ public class MbmsDownloadManager { public static MbmsDownloadManager create(Context context, MbmsDownloadManagerCallback listener, int subscriptionId) throws MbmsException { + if (!sIsInitialized.compareAndSet(false, true)) { + throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE); + } MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId); - mdm.bindAndInitialize(); + try { + mdm.bindAndInitialize(); + } catch (MbmsException e) { + sIsInitialized.set(false); + throw e; + } return mdm; } @@ -266,16 +185,27 @@ public class MbmsDownloadManager { result = downloadService.initialize(mSubscriptionId, mCallback); } catch (RemoteException e) { Log.e(LOG_TAG, "Service died before initialization"); + sIsInitialized.set(false); return; } catch (RuntimeException e) { Log.e(LOG_TAG, "Runtime exception during initialization"); - mCallback.error( + sendErrorToApp( MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, e.toString()); + sIsInitialized.set(false); return; } if (result != MbmsException.SUCCESS) { - mCallback.error(result, "Error returned during initialization"); + sendErrorToApp(result, "Error returned during initialization"); + sIsInitialized.set(false); + return; + } + try { + downloadService.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, + "Middleware lost during initialization"); + sIsInitialized.set(false); return; } mService.set(downloadService); @@ -283,6 +213,7 @@ public class MbmsDownloadManager { @Override public void onServiceDisconnected(ComponentName name) { + sIsInitialized.set(false); mService.set(null); } }); @@ -292,7 +223,7 @@ public class MbmsDownloadManager { * An inspection API to retrieve the list of available * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised. * The results are returned asynchronously via a call to - * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} + * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} * * The serviceClasses argument lets the app filter on types of programming and is opaque data * negotiated beforehand between the app and the carrier. @@ -306,7 +237,7 @@ public class MbmsDownloadManager { * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE} * * @param classList A list of service classes which the app wishes to receive - * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks + * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks * about. Subsequent calls to this method will replace this list of service * classes (i.e. the middleware will no longer send updates for services * matching classes only in the old list). @@ -336,14 +267,15 @@ public class MbmsDownloadManager { * local instance of {@link android.content.SharedPreferences} and by the middleware. * * If this method is not called at least once before calling - * {@link #download(DownloadRequest, IDownloadCallback)}, the framework + * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework * will default to a directory formed by the concatenation of the app's files directory and * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}. * * Before calling this method, the app must cancel all of its pending * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done, * an {@link MbmsException} will be thrown with code - * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} + * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the + * provided directory is the same as what has been previously configured. * * The {@link File} supplied as a root temp file directory must already exist. If not, an * {@link IllegalArgumentException} will be thrown. @@ -383,6 +315,26 @@ public class MbmsDownloadManager { prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply(); } + /** + * Retrieves the currently configured temp file root directory. Returns the file that was + * configured via {@link #setTempFileRootDirectory(File)} or the default directory + * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting + * the temp file root. If neither method has been called since the last time the app's shared + * preferences were reset, returns null. + * + * @return A {@link File} pointing to the configured temp file directory, or null if not yet + * configured. + */ + public @Nullable File getTempFileRootDirectory() { + SharedPreferences prefs = mContext.getSharedPreferences( + MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0); + String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null); + if (path != null) { + return new File(path); + } + return null; + } + /** * Requests a download of a file that is available via multicast. * @@ -404,7 +356,7 @@ public class MbmsDownloadManager { * @param progressListener Optional listener that will be provided progress updates * if the app is running. */ - public void download(DownloadRequest request, IDownloadProgressListener progressListener) + public void download(DownloadRequest request, DownloadProgressListener progressListener) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { @@ -434,7 +386,7 @@ public class MbmsDownloadManager { /** * Returns a list of pending {@link DownloadRequest}s that originated from this application. * A pending request is one that was issued via - * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through + * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through * {@link #cancelDownload(DownloadRequest)}. * @return A list, possibly empty, of {@link DownloadRequest}s */ @@ -550,43 +502,15 @@ public class MbmsDownloadManager { return; } downloadService.dispose(mSubscriptionId); - mService.set(null); } catch (RemoteException e) { // Ignore Log.i(LOG_TAG, "Remote exception while disposing of service"); + } finally { + mService.set(null); + sIsInitialized.set(false); } } - /** - * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that - * the various intents from the middleware should be targeted towards. - * @param uid The uid of the frontend app. - * @return The component name of the receiver that the middleware should send its intents to, - * or null if the app didn't declare it in the manifest. - * - * @hide - * future systemapi - */ - public static ComponentName getAppReceiverFromUid(Context context, int uid) { - String[] packageNames = context.getPackageManager().getPackagesForUid(uid); - if (packageNames == null) { - return null; - } - - for (String packageName : packageNames) { - ComponentName candidate = new ComponentName(packageName, - MbmsDownloadReceiver.class.getCanonicalName()); - Intent queryIntent = new Intent(); - queryIntent.setComponent(candidate); - List receivers = - context.getPackageManager().queryBroadcastReceivers(queryIntent, 0); - if (receivers != null && receivers.size() > 0) { - return candidate; - } - } - return null; - } - private void writeDownloadRequestToken(DownloadRequest request) { File token = getDownloadRequestTokenPath(request); if (!token.getParentFile().exists()) { @@ -651,4 +575,12 @@ public class MbmsDownloadManager { } } } + + private void sendErrorToApp(int errorCode, String message) { + try { + mCallback.error(errorCode, message); + } catch (RemoteException e) { + // Ignore, should not happen locally. + } + } } diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java index 911f83f0d8f1a..7a6631a3d90c9 100644 --- a/telephony/java/android/telephony/MbmsStreamingManager.java +++ b/telephony/java/android/telephony/MbmsStreamingManager.java @@ -16,11 +16,17 @@ package android.telephony; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; +import android.telephony.mbms.InternalStreamingManagerCallback; +import android.telephony.mbms.InternalStreamingServiceCallback; import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsStreamingManagerCallback; import android.telephony.mbms.MbmsUtils; @@ -31,6 +37,7 @@ import android.telephony.mbms.vendor.IMbmsStreamingService; import android.util.Log; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -41,21 +48,42 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; */ public class MbmsStreamingManager { private static final String LOG_TAG = "MbmsStreamingManager"; + + /** + * Service action which must be handled by the middleware implementing the MBMS streaming + * interface. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; + private static AtomicBoolean sIsInitialized = new AtomicBoolean(false); + private AtomicReference mService = new AtomicReference<>(null); - private MbmsStreamingManagerCallback mCallbackToApp; + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + sIsInitialized.set(false); + sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification"); + } + }; + + private InternalStreamingManagerCallback mInternalCallback; private final Context mContext; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; /** @hide */ private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback, - int subscriptionId) { + int subscriptionId, Handler handler) { mContext = context; - mCallbackToApp = callback; mSubscriptionId = subscriptionId; + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } + mInternalCallback = new InternalStreamingManagerCallback(callback, handler); } /** @@ -65,27 +93,62 @@ public class MbmsStreamingManager { * main thread. This may throw an {@link MbmsException}, indicating errors that may happen * during the initialization or binding process. * + * + * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this + * method while there is an active instance of {@link MbmsStreamingManager} in your process + * (in other words, one that has not had {@link #dispose()} called on it), this method will + * throw an {@link MbmsException}. If you call this method in a different process + * running under the same UID, an error will be indicated via + * {@link MbmsStreamingManagerCallback#onError(int, String)}. + * + * Note that initialization may fail asynchronously. If you wish to try again after you + * receive such an asynchronous error, you must call dispose() on the instance of + * {@link MbmsStreamingManager} that you received before calling this method again. + * * @param context The {@link Context} to use. * @param callback A callback object on which you wish to receive results of asynchronous * operations. * @param subscriptionId The subscription ID to use. + * @param handler The handler you wish to receive callbacks on. If null, callbacks will be + * processed on the main looper (in other words, the looper returned from + * {@link Looper#getMainLooper()}). */ public static MbmsStreamingManager create(Context context, - MbmsStreamingManagerCallback callback, int subscriptionId) + MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler) throws MbmsException { - MbmsStreamingManager manager = new MbmsStreamingManager(context, callback, subscriptionId); - manager.bindAndInitialize(); + if (!sIsInitialized.compareAndSet(false, true)) { + throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE); + } + MbmsStreamingManager manager = new MbmsStreamingManager(context, callback, + subscriptionId, handler); + try { + manager.bindAndInitialize(); + } catch (MbmsException e) { + sIsInitialized.set(false); + throw e; + } return manager; } /** * Create a new MbmsStreamingManager using the system default data subscription ID. - * See {@link #create(Context, MbmsStreamingManagerCallback, int)}. + * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}. + */ + public static MbmsStreamingManager create(Context context, + MbmsStreamingManagerCallback callback, Handler handler) + throws MbmsException { + return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler); + } + + /** + * Create a new MbmsStreamingManager using the system default data subscription ID and + * default {@link Handler}. + * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}. */ public static MbmsStreamingManager create(Context context, MbmsStreamingManagerCallback callback) throws MbmsException { - return create(context, callback, SubscriptionManager.getDefaultSubscriptionId()); + return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null); } /** @@ -95,17 +158,19 @@ public class MbmsStreamingManager { * May throw an {@link IllegalStateException} */ public void dispose() { - IMbmsStreamingService streamingService = mService.get(); - if (streamingService == null) { - // Ignore and return, assume already disposed. - return; - } try { + IMbmsStreamingService streamingService = mService.get(); + if (streamingService == null) { + // Ignore and return, assume already disposed. + return; + } streamingService.dispose(mSubscriptionId); } catch (RemoteException e) { // Ignore for now + } finally { + mService.set(null); + sIsInitialized.set(false); } - mService.set(null); } /** @@ -139,16 +204,17 @@ public class MbmsStreamingManager { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); + sIsInitialized.set(false); throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } /** - * Starts streaming a requested service, reporting status to the indicated listener. + * Starts streaming a requested service, reporting status to the indicated callback. * Returns an object used to control that stream. The stream may not be ready for consumption * immediately upon return from this method -- wait until the streaming state has been * reported via - * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)} + * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)} * * May throw an * {@link MbmsException} containing any of the error codes in @@ -158,34 +224,44 @@ public class MbmsStreamingManager { * * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * - * Asynchronous errors through the listener include any of the errors in + * Asynchronous errors through the callback include any of the errors in * {@link android.telephony.mbms.MbmsException.GeneralErrors} or * {@link android.telephony.mbms.MbmsException.StreamingErrors}. * * @param serviceInfo The information about the service to stream. - * @param listener A listener that'll be called when something about the stream changes. + * @param callback A callback that'll be called when something about the stream changes. + * @param handler A handler that calls to {@code callback} should be called on. If null, + * defaults to the handler provided via + * {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}. * @return An instance of {@link StreamingService} through which the stream can be controlled. */ public StreamingService startStreaming(StreamingServiceInfo serviceInfo, - StreamingServiceCallback listener) throws MbmsException { + StreamingServiceCallback callback, Handler handler) throws MbmsException { IMbmsStreamingService streamingService = mService.get(); if (streamingService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } + InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback( + callback, handler == null ? mInternalCallback.getHandler() : handler); + + StreamingService serviceForApp = new StreamingService( + mSubscriptionId, streamingService, serviceInfo, serviceCallback); + try { int returnCode = streamingService.startStreaming( - mSubscriptionId, serviceInfo.getServiceId(), listener); + mSubscriptionId, serviceInfo.getServiceId(), serviceCallback); if (returnCode != MbmsException.SUCCESS) { throw new MbmsException(returnCode); } } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); + sIsInitialized.set(false); throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } - return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener); + return serviceForApp; } private void bindAndInitialize() throws MbmsException { @@ -197,19 +273,34 @@ public class MbmsStreamingManager { IMbmsStreamingService.Stub.asInterface(service); int result; try { - result = streamingService.initialize(mCallbackToApp, mSubscriptionId); + result = streamingService.initialize(mInternalCallback, + mSubscriptionId); } catch (RemoteException e) { Log.e(LOG_TAG, "Service died before initialization"); + sendErrorToApp( + MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, + e.toString()); + sIsInitialized.set(false); return; } catch (RuntimeException e) { Log.e(LOG_TAG, "Runtime exception during initialization"); - mCallbackToApp.error( + sendErrorToApp( MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, e.toString()); + sIsInitialized.set(false); return; } if (result != MbmsException.SUCCESS) { - mCallbackToApp.error(result, "Error returned during initialization"); + sendErrorToApp(result, "Error returned during initialization"); + sIsInitialized.set(false); + return; + } + try { + streamingService.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, + "Middleware lost during initialization"); + sIsInitialized.set(false); return; } mService.set(streamingService); @@ -217,8 +308,17 @@ public class MbmsStreamingManager { @Override public void onServiceDisconnected(ComponentName name) { + sIsInitialized.set(false); mService.set(null); } }); } + + private void sendErrorToApp(int errorCode, String message) { + try { + mInternalCallback.error(errorCode, message); + } catch (RemoteException e) { + // Ignore, should not happen locally. + } + } } diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadProgressListener.java index d6bd5dca87819..d91e9ad24a9ac 100644 --- a/telephony/java/android/telephony/mbms/DownloadProgressListener.java +++ b/telephony/java/android/telephony/mbms/DownloadProgressListener.java @@ -16,6 +16,8 @@ package android.telephony.mbms; +import android.os.RemoteException; + /** * A optional listener class used by download clients to track progress. * @hide @@ -38,8 +40,9 @@ public class DownloadProgressListener extends IDownloadProgressListener.Stub { * @param currentDecodedSize is the number of bytes that have been decoded. * @param fullDecodedSize is the total number of bytes that make up the final decoded content. */ + @Override public void progress(DownloadRequest request, FileInfo fileInfo, int currentDownloadSize, int fullDownloadSize, - int currentDecodedSize, int fullDecodedSize) { + int currentDecodedSize, int fullDecodedSize) throws RemoteException { } } diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index 01e0bbdfc0a0a..eae9011e42c8a 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -25,6 +25,7 @@ import android.util.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -77,12 +78,18 @@ public class DownloadRequest implements Parcelable { private String appIntent; private int version = CURRENT_VERSION; + /** + * Sets the service from which the download request to be built will download from. + * @param serviceInfo + * @return + */ public Builder setServiceInfo(FileServiceInfo serviceInfo) { fileServiceId = serviceInfo.getServiceId(); return this; } /** + * Set the service ID for the download request. For use by the middleware only. * @hide * TODO: systemapi */ @@ -91,11 +98,23 @@ public class DownloadRequest implements Parcelable { return this; } + /** + * Sets the source URI for the download request to be built. + * @param source + * @return + */ public Builder setSource(Uri source) { this.source = source; return this; } + /** + * Sets the destination URI for the download request to be built. The middleware should + * not set this directly. + * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested + * final destination of the download. + * @return + */ public Builder setDest(Uri dest) { if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) { throw new IllegalArgumentException("Destination uri must not exceed length " + @@ -105,11 +124,25 @@ public class DownloadRequest implements Parcelable { return this; } - public Builder setSubscriptionId(int sub) { - this.subscriptionId = sub; + /** + * Set the subscription ID on which the file(s) should be downloaded. + * @param subscriptionId + * @return + */ + public Builder setSubscriptionId(int subscriptionId) { + this.subscriptionId = subscriptionId; return this; } + /** + * Set the {@link Intent} that should be sent when the download completes or fails. This + * should be an intent with a explicit {@link android.content.ComponentName} targeted to a + * {@link android.content.BroadcastReceiver} in the app's package. + * + * The middleware should not use this method. + * @param intent + * @return + */ public Builder setAppIntent(Intent intent) { this.appIntent = intent.toUri(0); if (this.appIntent.length() > MAX_APP_INTENT_SIZE) { @@ -120,7 +153,12 @@ public class DownloadRequest implements Parcelable { } /** - * For use by middleware only + * For use by the middleware to set the byte array of opaque data. The opaque data + * includes information about the download request that is used by the client app and the + * manager code, but is irrelevant to the middleware. + * @param data A byte array, the contents of which should have been originally obtained + * from {@link DownloadRequest#getOpaqueData()}. + * @return * TODO: systemapi * @hide */ @@ -201,22 +239,40 @@ public class DownloadRequest implements Parcelable { out.writeInt(version); } + /** + * @return The ID of the file service to download from. + */ public String getFileServiceId() { return fileServiceId; } + /** + * @return The source URI to download from + */ public Uri getSourceUri() { return sourceUri; } + /** + * For use by the client app only. + * @return The URI of the final destination of the download. + */ public Uri getDestinationUri() { return destinationUri; } + /** + * @return The subscription ID on which to perform MBMS operations. + */ public int getSubscriptionId() { return subscriptionId; } + /** + * For internal use -- returns the intent to send to the app after download completion or + * failure. + * @hide + */ public Intent getIntentForApp() { try { return Intent.parseUri(serializedResultIntentForApp, 0); @@ -226,6 +282,10 @@ public class DownloadRequest implements Parcelable { } /** + * For use by the middleware only. The byte array returned from this method should be + * persisted and sent back to the app upon download completion or failure by passing it into + * {@link Builder#setOpaqueData(byte[])}. + * @return A byte array of opaque data to persist. * @hide * TODO: systemapi */ diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java index 1b873938a3f20..f97131dda4271 100644 --- a/telephony/java/android/telephony/mbms/FileInfo.java +++ b/telephony/java/android/telephony/mbms/FileInfo.java @@ -38,16 +38,6 @@ public class FileInfo implements Parcelable { */ private final String mimeType; - /** - * The size of the file in bytes. - */ - private final long size; - - /** - * The MD5 hash of the file. - */ - private final byte md5Hash[]; - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -61,29 +51,24 @@ public class FileInfo implements Parcelable { } }; - public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) { + /** + * @hide + * TODO: systemapi + */ + public FileInfo(Uri uri, String mimeType) { this.uri = uri; this.mimeType = mimeType; - this.size = size; - this.md5Hash = md5Hash; } private FileInfo(Parcel in) { uri = in.readParcelable(null); mimeType = in.readString(); - size = in.readLong(); - int arraySize = in.readInt(); - md5Hash = new byte[arraySize]; - in.readByteArray(md5Hash); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(uri, flags); dest.writeString(mimeType); - dest.writeLong(size); - dest.writeInt(md5Hash.length); - dest.writeByteArray(md5Hash); } @Override @@ -98,12 +83,4 @@ public class FileInfo implements Parcelable { public String getMimeType() { return mimeType; } - - public long getSize() { - return size; - } - - public byte[] getMd5Hash() { - return md5Hash; - } } diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java index 6646dc8a56dfb..8afe4d3c52302 100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.java +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java @@ -32,6 +32,7 @@ import java.util.Map; public class FileServiceInfo extends ServiceInfo implements Parcelable { private final List files; + /** @hide TODO: systemapi */ public FileServiceInfo(Map newNames, String newClassName, List newLocales, String newServiceId, Date start, Date end, List newFiles) { diff --git a/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java new file mode 100644 index 0000000000000..b52df8c0dd84f --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms; + +import android.os.Handler; +import android.os.RemoteException; +import android.telephony.mbms.IMbmsStreamingManagerCallback; +import android.telephony.mbms.MbmsStreamingManagerCallback; +import android.telephony.mbms.StreamingServiceInfo; + +import java.util.List; + +/** @hide */ +public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub { + private final Handler mHandler; + private final MbmsStreamingManagerCallback mAppCallback; + + public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback, + Handler handler) { + mAppCallback = appCallback; + mHandler = handler; + } + + @Override + public void error(int errorCode, String message) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onError(errorCode, message); + } + }); + } + + @Override + public void streamingServicesUpdated(List services) + throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onStreamingServicesUpdated(services); + } + }); + } + + @Override + public void middlewareReady() throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onMiddlewareReady(); + } + }); + } + + public Handler getHandler() { + return mHandler; + } +} diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java new file mode 100644 index 0000000000000..bb337b271cf0a --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms; + +import android.os.Handler; +import android.os.RemoteException; + +/** @hide */ +public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub { + private final StreamingServiceCallback mAppCallback; + private final Handler mHandler; + + public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) { + mAppCallback = appCallback; + mHandler = handler; + } + + @Override + public void error(int errorCode, String message) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onError(errorCode, message); + } + }); + } + + @Override + public void streamStateUpdated(int state, int reason) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onStreamStateUpdated(state, reason); + } + }); + } + + @Override + public void mediaDescriptionUpdated() throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onMediaDescriptionUpdated(); + } + }); + } + + @Override + public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength); + } + }); + } + + @Override + public void streamMethodUpdated(int methodType) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + mAppCallback.onStreamMethodUpdated(methodType); + } + }); + } +} diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java index ba25f663ffb44..17291d09215df 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java @@ -16,6 +16,9 @@ package android.telephony.mbms; +import android.os.RemoteException; +import android.telephony.MbmsDownloadManager; + import java.util.List; /** @@ -24,12 +27,8 @@ import java.util.List; */ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub { - public final static int ERROR_CARRIER_NOT_SUPPORTED = 1; - public final static int ERROR_UNABLE_TO_INITIALIZE = 2; - public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY = 3; - - - public void error(int errorCode, String message) { + @Override + public void error(int errorCode, String message) throws RemoteException { // default implementation empty } @@ -45,7 +44,8 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St * @param services a List of FileServiceInfos * */ - public void fileServicesUpdated(List services) { + @Override + public void fileServicesUpdated(List services) throws RemoteException { // default implementation empty } @@ -58,7 +58,7 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} */ @Override - public void middlewareReady() { + public void middlewareReady() throws RemoteException { // default implementation empty } } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java index 339ff3985bffc..ba7d120a3b7c8 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.telephony.MbmsDownloadManager; +import android.telephony.mbms.vendor.VendorIntents; import android.util.Log; import java.io.File; @@ -56,9 +57,9 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { /** * Indicates that the intent sent had an invalid action. This will be the result if * {@link Intent#getAction()} returns anything other than - * {@link MbmsDownloadManager#ACTION_DOWNLOAD_RESULT_INTERNAL}, - * {@link MbmsDownloadManager#ACTION_FILE_DESCRIPTOR_REQUEST}, or - * {@link MbmsDownloadManager#ACTION_CLEANUP}. + * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL}, + * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or + * {@link VendorIntents#ACTION_CLEANUP}. * This is a fatal result code and no result extras should be expected. */ public static final int RESULT_INVALID_ACTION = 1; @@ -70,7 +71,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { public static final int RESULT_MALFORMED_INTENT = 2; /** - * Indicates that the supplied value for {@link MbmsDownloadManager#EXTRA_TEMP_FILE_ROOT} + * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT} * does not match what the app has stored. * This is a fatal result code and no result extras should be expected. */ @@ -104,18 +105,18 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { setResultCode(RESULT_MALFORMED_INTENT); return; } - if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT), + if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT), MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) { setResultCode(RESULT_BAD_TEMP_FILE_ROOT); return; } - if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { + if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { moveDownloadedFile(context, intent); cleanupPostMove(context, intent); - } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { + } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { generateTempFiles(context, intent); - } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) { + } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) { cleanupTempFiles(context, intent); } else { setResultCode(RESULT_INVALID_ACTION); @@ -123,16 +124,16 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { } private boolean verifyIntentContents(Context context, Intent intent) { - if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { + if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) { Log.w(LOG_TAG, "Download result did not include a result code. Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_REQUEST)) { + if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) { Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) { + if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) { Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring."); return false; } @@ -141,12 +142,12 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { "Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FINAL_URI)) { + if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) { Log.w(LOG_TAG, "Download result did not include the path to the final " + "temp file. Ignoring."); return false; } - DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST); String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX; File expectedTokenFile = new File( MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()), @@ -156,27 +157,27 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { "Expected " + expectedTokenFile); return false; } - } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) { + } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { + if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) { Log.w(LOG_TAG, "Temp file request did not include the associated service info." + " Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) { + if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) { Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring."); return false; } - } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) { - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) { + } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) { + if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) { Log.w(LOG_TAG, "Cleanup request did not include the associated service info." + " Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) { + if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) { Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE)) { + if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) { Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " + "Ignoring."); return false; @@ -186,7 +187,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { } private void moveDownloadedFile(Context context, Intent intent) { - DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST); Intent intentForApp = request.getIntentForApp(); int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT, @@ -200,7 +201,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { } Uri destinationUri = request.getDestinationUri(); - Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI); + Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI); if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) { Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile); setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR); @@ -225,13 +226,13 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { } private void cleanupPostMove(Context context, Intent intent) { - DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST); if (request == null) { Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring."); return; } - List tempFiles = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_TEMP_LIST); + List tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST); if (tempFiles == null) { return; } @@ -246,15 +247,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { private void generateTempFiles(Context context, Intent intent) { FileServiceInfo serviceInfo = - intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO); + intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO); if (serviceInfo == null) { Log.w(LOG_TAG, "Temp file request did not include the associated service info. " + "Ignoring."); setResultCode(RESULT_MALFORMED_INTENT); return; } - int fdCount = intent.getIntExtra(MbmsDownloadManager.EXTRA_FD_COUNT, 0); - List pausedList = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_PAUSED_LIST); + int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0); + List pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST); if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) { Log.i(LOG_TAG, "No temp files actually requested. Ending."); @@ -269,8 +270,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { generateUrisForPausedFiles(context, serviceInfo, pausedList); Bundle result = new Bundle(); - result.putParcelableArrayList(MbmsDownloadManager.EXTRA_FREE_URI_LIST, freshTempFiles); - result.putParcelableArrayList(MbmsDownloadManager.EXTRA_PAUSED_URI_LIST, pausedFiles); + result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles); + result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles); setResultCode(RESULT_OK); setResultExtras(result); } @@ -353,11 +354,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { private void cleanupTempFiles(Context context, Intent intent) { FileServiceInfo serviceInfo = - intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO); + intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO); File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo.getServiceId()); final List filesInUse = - intent.getParcelableArrayListExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE); + intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE); File[] filesToDelete = tempFileDir.listFiles(new FileFilter() { @Override public boolean accept(File file) { diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java index 8888119f90e67..7cf87927dca1a 100644 --- a/telephony/java/android/telephony/mbms/MbmsException.java +++ b/telephony/java/android/telephony/mbms/MbmsException.java @@ -31,7 +31,7 @@ public class MbmsException extends Exception { /** * Indicates that the app attempted to perform an operation on an instance of - * {@link android.telephony.MbmsDownloadManager} or + * TODO: link android.telephony.MbmsDownloadManager or * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware. */ public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; @@ -44,10 +44,11 @@ public class MbmsException extends Exception { * middleware. They are applicable to both streaming and file-download use-cases. */ public static class InitializationErrors { + private InitializationErrors() {} /** * Indicates that the app tried to create more than one instance each of * {@link android.telephony.MbmsStreamingManager} or - * {@link android.telephony.MbmsDownloadManager}. + * TODO: link android.telephony.MbmsDownloadManager */ public static final int ERROR_DUPLICATE_INITIALIZE = 101; /** Indicates that the app is not authorized to access media via MBMS.*/ @@ -61,10 +62,11 @@ public class MbmsException extends Exception { * streaming and file-download. */ public static class GeneralErrors { + private GeneralErrors() {} /** * Indicates that the app attempted to perform an operation before receiving notification - * that the middleware is ready via {@link MbmsStreamingManagerCallback#middlewareReady()} - * or {@link MbmsDownloadManagerCallback#middlewareReady()}. + * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()} + * or TODO: link MbmsDownloadManagerCallback#middlewareReady */ public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201; /** @@ -97,6 +99,7 @@ public class MbmsException extends Exception { * Indicates the errors that are applicable only to the streaming use-case */ public static class StreamingErrors { + private StreamingErrors() {} /** Indicates that the middleware cannot start a stream due to too many ongoing streams */ public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301; @@ -105,7 +108,8 @@ public class MbmsException extends Exception { /** * Indicates that the app called - * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)} + * {@link android.telephony.MbmsStreamingManager#startStreaming( + * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)} * more than once for the same {@link StreamingServiceInfo}. */ public static final int ERROR_DUPLICATE_START_STREAM = 303; @@ -113,6 +117,8 @@ public class MbmsException extends Exception { /** * Indicates the errors that are applicable only to the file-download use-case + * TODO: unhide + * @hide */ public static class DownloadErrors { /** @@ -127,9 +133,7 @@ public class MbmsException extends Exception { private final int mErrorCode; - /** @hide - * TODO: future systemapi - */ + /** @hide */ public MbmsException(int errorCode) { super(); mErrorCode = errorCode; diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java index 2e91be9acaf7f..831050efdd477 100644 --- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java @@ -16,20 +16,26 @@ package android.telephony.mbms; +import android.content.Context; +import android.os.RemoteException; +import android.telephony.MbmsStreamingManager; + import java.util.List; /** - * A Parcelable class with Cell-Broadcast service information. + * A callback class that is used to receive information from the middleware on MBMS streaming + * services. An instance of this object should be passed into + * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}. * @hide */ -public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub { - - public final static int ERROR_CARRIER_NOT_SUPPORTED = 1; - public final static int ERROR_UNABLE_TO_INITIALIZE = 2; - public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY = 3; - - - public void error(int errorCode, String message) { +public class MbmsStreamingManagerCallback { + /** + * Called by the middleware when it has detected an error condition. The possible error codes + * are listed in {@link MbmsException}. + * @param errorCode The error code. + * @param message A human-readable message generated by the middleware for debugging purposes. + */ + public void onError(int errorCode, String message) { // default implementation empty } @@ -45,7 +51,7 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback. * @param services a List of StreamingServiceInfos * */ - public void streamingServicesUpdated(List services) { + public void onStreamingServicesUpdated(List services) { // default implementation empty } @@ -57,8 +63,7 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback. * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} */ - @Override - public void middlewareReady() { + public void onMiddlewareReady() { // default implementation empty } } diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java index f9ad44c63118b..423ae01df7152 100644 --- a/telephony/java/android/telephony/mbms/ServiceInfo.java +++ b/telephony/java/android/telephony/mbms/ServiceInfo.java @@ -30,43 +30,22 @@ import java.util.Objects; import java.util.Set; /** - * A Parcelable class with Cell-Broadcast service information. + * Describes a cell-broadcast service. This class should not be instantiated directly -- use + * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden * @hide */ -public class ServiceInfo implements Parcelable { +public class ServiceInfo { // arbitrary limit on the number of locale -> name pairs we support final static int MAP_LIMIT = 1000; - /** - * User displayable names listed by language. Unmodifiable. - */ - final Map names; - - /** - * The class name for this service - used to catagorize and filter - */ - final String className; - - /** - * The languages available for this service content - */ - final List locales; - - /** - * The carrier's identifier for the service. - */ - final String serviceId; - - /** - * The start time indicating when this service will be available. - */ - final Date sessionStartTime; - - /** - * The end time indicating when this sesion stops being available. - */ - final Date sessionEndTime; + private final Map names; + private final String className; + private final List locales; + private final String serviceId; + private final Date sessionStartTime; + private final Date sessionEndTime; + /** @hide */ public ServiceInfo(Map newNames, String newClassName, List newLocales, String newServiceId, Date start, Date end) { if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName) @@ -89,20 +68,8 @@ public class ServiceInfo implements Parcelable { sessionEndTime = (Date)end.clone(); } - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public FileServiceInfo createFromParcel(Parcel source) { - return new FileServiceInfo(source); - } - - @Override - public FileServiceInfo[] newArray(int size) { - return new FileServiceInfo[size]; - } - }; - - ServiceInfo(Parcel in) { + /** @hide */ + protected ServiceInfo(Parcel in) { int mapCount = in.readInt(); if (mapCount > MAP_LIMIT || mapCount < 0) { throw new RuntimeException("bad map length" + mapCount); @@ -128,7 +95,7 @@ public class ServiceInfo implements Parcelable { sessionEndTime = (java.util.Date) in.readSerializable(); } - @Override + /** @hide */ public void writeToParcel(Parcel dest, int flags) { Set keySet = names.keySet(); dest.writeInt(keySet.size()); @@ -147,31 +114,44 @@ public class ServiceInfo implements Parcelable { dest.writeSerializable(sessionEndTime); } - @Override - public int describeContents() { - return 0; - } - + /** + * User displayable names listed by language. Do not modify the map returned from this method. + */ public Map getNames() { return names; } + /** + * The class name for this service - used to categorize and filter + */ public String getClassName() { return className; } + /** + * The languages available for this service content + */ public List getLocales() { return locales; } + /** + * The carrier's identifier for the service. + */ public String getServiceId() { return serviceId; } + /** + * The start time indicating when this service will be available. + */ public Date getSessionStartTime() { return sessionStartTime; } + /** + * The end time indicating when this session stops being available. + */ public Date getSessionEndTime() { return sessionEndTime; } diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java index c49f8a980cbba..5c4b7862289f7 100644 --- a/telephony/java/android/telephony/mbms/StreamingService.java +++ b/telephony/java/android/telephony/mbms/StreamingService.java @@ -26,13 +26,17 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** + * Class used to represent a single MBMS stream. After a stream has been started with + * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, + * StreamingServiceCallback, android.os.Handler)}, + * this class is used to hold information about the stream and control it. * @hide */ public class StreamingService { private static final String LOG_TAG = "MbmsStreamingService"; /** - * The state of a stream, reported via {@link StreamingServiceCallback#streamStateUpdated} + * The state of a stream, reported via {@link StreamingServiceCallback#onStreamStateUpdated} * @hide */ @Retention(RetentionPolicy.SOURCE) @@ -44,7 +48,7 @@ public class StreamingService { /** * The reason for a stream state change, reported via - * {@link StreamingServiceCallback#streamStateUpdated} + * {@link StreamingServiceCallback#onStreamStateUpdated} * @hide */ @Retention(RetentionPolicy.SOURCE) @@ -60,7 +64,8 @@ public class StreamingService { /** * State changed due to a call to {@link #stopStreaming()} or - * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)} + * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, + * StreamingServiceCallback, android.os.Handler)} */ public static final int REASON_BY_USER_REQUEST = 1; @@ -87,27 +92,28 @@ public class StreamingService { /** * State changed due to the device leaving the where this stream is being broadcast. */ - public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 5; + public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; /** * The method of transmission currently used for a stream, - * reported via {@link StreamingServiceCallback#streamMethodUpdated} + * reported via {@link StreamingServiceCallback#onStreamMethodUpdated} */ public final static int BROADCAST_METHOD = 1; public final static int UNICAST_METHOD = 2; private final int mSubscriptionId; private final StreamingServiceInfo mServiceInfo; - private final IStreamingServiceCallback mCallback; + private final InternalStreamingServiceCallback mCallback; private IMbmsStreamingService mService; + /** * @hide */ public StreamingService(int subscriptionId, IMbmsStreamingService service, StreamingServiceInfo streamingServiceInfo, - IStreamingServiceCallback callback) { + InternalStreamingServiceCallback callback) { mSubscriptionId = subscriptionId; mService = service; mServiceInfo = streamingServiceInfo; diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java index cab9c23499eaf..eeef8bcab04fa 100644 --- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java +++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java @@ -17,10 +17,11 @@ package android.telephony.mbms; /** - * A Callback class for use when the application is actively streaming content. + * A callback class for use when the application is actively streaming content. The middleware + * will provide updates on the status of the stream via this callback. * @hide */ -public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { +public class StreamingServiceCallback { /** * Indicates broadcast signal strength is not available for this service. @@ -31,8 +32,13 @@ public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { */ public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; - @Override - public void error(int errorCode, String message) { + /** + * Called by the middleware when it has detected an error condition in this stream. The + * possible error codes are listed in {@link MbmsException}. + * @param errorCode The error code. + * @param message A human-readable message generated by the middleware for debugging purposes. + */ + public void onError(int errorCode, String message) { // default implementation empty } @@ -42,8 +48,7 @@ public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED} * and {@link StreamingService#STATE_STALLED}. */ - @Override - public void streamStateUpdated(@StreamingService.StreamingState int state, + public void onStreamStateUpdated(@StreamingService.StreamingState int state, @StreamingService.StreamingStateChangeReason int reason) { // default implementation empty } @@ -58,8 +63,7 @@ public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { * This may be called when a looping stream hits the end or * when parameters have changed to account for time drift. */ - @Override - public void mediaDescriptionUpdated() { + public void onMediaDescriptionUpdated() { // default implementation empty } @@ -73,8 +77,7 @@ public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available * for this service due to timing, geography or popularity. */ - @Override - public void broadcastSignalStrengthUpdated(int signalStrength) { + public void onBroadcastSignalStrengthUpdated(int signalStrength) { // default implementation empty } @@ -94,8 +97,7 @@ public class StreamingServiceCallback extends IStreamingServiceCallback.Stub { * See {@link StreamingService#BROADCAST_METHOD} and * {@link StreamingService#UNICAST_METHOD} */ - @Override - public void streamMethodUpdated(int methodType) { + public void onStreamMethodUpdated(int methodType) { // default implementation empty } } diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java index 77ce3bbd696e4..8e7917a6992ec 100644 --- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java +++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java @@ -16,6 +16,7 @@ package android.telephony.mbms; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -25,15 +26,25 @@ import java.util.Locale; import java.util.Map; /** - * A Parcelable class Cell-Broadcast media stream information. - * This may not have any more info than ServiceInfo, but kept for completeness. + * Describes a single MBMS streaming service. * @hide */ -public class StreamingServiceInfo extends ServiceInfo implements Parcelable { +public final class StreamingServiceInfo extends ServiceInfo implements Parcelable { - public StreamingServiceInfo(Map newNames, String newClassName, - List newLocales, String newServiceId, Date start, Date end) { - super(newNames, newClassName, newLocales, newServiceId, start, end); + /** + * @param names User displayable names listed by language. + * @param className The class name for this service - used by frontend apps to categorize and + * filter. + * @param locales The languages available for this service content. + * @param serviceId The carrier's identifier for the service. + * @param start The start time indicating when this service will be available. + * @param end The end time indicating when this session stops being available. + * @hide + */ + @SystemApi + public StreamingServiceInfo(Map names, String className, + List locales, String serviceId, Date start, Date end) { + super(names, className, locales, serviceId, start, end); } public static final Parcelable.Creator CREATOR = @@ -49,7 +60,7 @@ public class StreamingServiceInfo extends ServiceInfo implements Parcelable { } }; - StreamingServiceInfo(Parcel in) { + private StreamingServiceInfo(Parcel in) { super(in); } diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index edd585808580f..71713d013f975 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -18,10 +18,13 @@ package android.telephony.mbms.vendor; import android.annotation.NonNull; import android.os.RemoteException; +import android.telephony.mbms.DownloadProgressListener; import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.FileInfo; +import android.telephony.mbms.FileServiceInfo; import android.telephony.mbms.IDownloadProgressListener; import android.telephony.mbms.IMbmsDownloadManagerCallback; +import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsException; import java.util.List; @@ -44,15 +47,39 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via * {@link IMbmsDownloadManagerCallback#error(int, String)}. * - * @param listener The callback to use to communicate with the app. + * @param callback The callback to use to communicate with the app. * @param subscriptionId The subscription ID to use. */ - @Override - public int initialize(int subscriptionId, - IMbmsDownloadManagerCallback listener) throws RemoteException { + public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback) + throws RemoteException { return 0; } + /** + * Actual AIDL implementation -- hides the callback AIDL from the API. + * @hide + */ + @Override + public final int initialize(int subscriptionId, + final IMbmsDownloadManagerCallback callback) throws RemoteException { + return initialize(subscriptionId, new MbmsDownloadManagerCallback() { + @Override + public void error(int errorCode, String message) throws RemoteException { + callback.error(errorCode, message); + } + + @Override + public void fileServicesUpdated(List services) throws RemoteException { + callback.fileServicesUpdated(services); + } + + @Override + public void middlewareReady() throws RemoteException { + callback.middlewareReady(); + } + }); + } + /** * Registers serviceClasses of interest with the appName/subId key. * Starts async fetching data on streaming services of matching classes to be reported @@ -107,19 +134,36 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * @param downloadRequest An object describing the set of files to be downloaded. * @param listener A listener through which the middleware can provide progress updates to * the app while both are still running. - * @return TODO: enumerate possible return values + * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors} + * or {@link MbmsException#SUCCESS} + */ + public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) { + return 0; + } + + /** + * Actual AIDL implementation -- hides the callback AIDL from the API. + * @hide */ @Override - public int download(DownloadRequest downloadRequest, IDownloadProgressListener listener) + public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener) throws RemoteException { - return 0; + return download(downloadRequest, new DownloadProgressListener() { + @Override + public void progress(DownloadRequest request, FileInfo fileInfo, int + currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int + fullDecodedSize) throws RemoteException { + listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize, + currentDecodedSize, fullDecodedSize); + } + }); } /** * Returns a list of pending {@link DownloadRequest}s that originated from the calling * application, identified by its uid. A pending request is one that was issued via - * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through + * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through * {@link #cancelDownload(DownloadRequest)}. * The middleware must return a non-null result synchronously or throw an exception * inheriting from {@link RuntimeException}. diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java index 585d5b9610b76..8f2786f864ac5 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java @@ -17,23 +17,29 @@ package android.telephony.mbms.vendor; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; +import android.os.Binder; import android.os.RemoteException; import android.telephony.mbms.IMbmsStreamingManagerCallback; import android.telephony.mbms.IStreamingServiceCallback; import android.telephony.mbms.MbmsException; +import android.telephony.mbms.MbmsStreamingManagerCallback; +import android.telephony.mbms.StreamingService; +import android.telephony.mbms.StreamingServiceCallback; +import android.telephony.mbms.StreamingServiceInfo; import java.util.List; /** * @hide - * TODO: future systemapi */ +//@SystemApi public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { /** * Initialize streaming service for this app and subId, registering the listener. * - * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which + * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which * will be intercepted and passed to the app as * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} * @@ -41,15 +47,60 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via * {@link IMbmsStreamingManagerCallback#error(int, String)}. * - * @param listener The callback to use to communicate with the app. + * @param callback The callback to use to communicate with the app. * @param subscriptionId The subscription ID to use. */ - @Override - public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId) + public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId) throws RemoteException { return 0; } + /** + * Actual AIDL implementation that hides the callback AIDL from the middleware. + * @hide + */ + @Override + public final int initialize(final IMbmsStreamingManagerCallback callback, + final int subscriptionId) throws RemoteException { + final int uid = Binder.getCallingUid(); + callback.asBinder().linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + onAppCallbackDied(uid, subscriptionId); + } + }, 0); + + return initialize(new MbmsStreamingManagerCallback() { + @Override + public void onError(int errorCode, String message) { + try { + callback.error(errorCode, message); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onStreamingServicesUpdated(List services) { + try { + callback.streamingServicesUpdated(services); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onMiddlewareReady() { + try { + callback.middlewareReady(); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + }, subscriptionId); + } + + /** * Registers serviceClasses of interest with the appName/subId key. * Starts async fetching data on streaming services of matching classes to be reported @@ -82,13 +133,77 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { * * @param subscriptionId The subscription id to use. * @param serviceId The ID of the streaming service that the app has requested. - * @param listener The listener object on which the app wishes to receive updates. + * @param callback The callback object on which the app wishes to receive updates. * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors} */ + public int startStreaming(int subscriptionId, String serviceId, + StreamingServiceCallback callback) throws RemoteException { + return 0; + } + + /** + * Actual AIDL implementation of startStreaming that hides the callback AIDL from the + * middleware. + * @hide + */ @Override public int startStreaming(int subscriptionId, String serviceId, - IStreamingServiceCallback listener) throws RemoteException { - return 0; + IStreamingServiceCallback callback) throws RemoteException { + final int uid = Binder.getCallingUid(); + callback.asBinder().linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + onAppCallbackDied(uid, subscriptionId); + } + }, 0); + + return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() { + @Override + public void onError(int errorCode, String message) { + try { + callback.error(errorCode, message); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onStreamStateUpdated(@StreamingService.StreamingState int state, + @StreamingService.StreamingStateChangeReason int reason) { + try { + callback.streamStateUpdated(state, reason); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onMediaDescriptionUpdated() { + try { + callback.mediaDescriptionUpdated(); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onBroadcastSignalStrengthUpdated(int signalStrength) { + try { + callback.broadcastSignalStrengthUpdated(signalStrength); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onStreamMethodUpdated(int methodType) { + try { + callback.streamMethodUpdated(methodType); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + }); } /** @@ -153,4 +268,12 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { @Override public void dispose(int subscriptionId) throws RemoteException { } + + /** + * Indicates that the app identified by the given UID and subscription ID has died. + * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}. + * @param subscriptionId The subscription ID the app is using. + */ + public void onAppCallbackDied(int uid, int subscriptionId) { + } } diff --git a/telephony/java/android/telephony/mbms/vendor/VendorIntents.java b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java new file mode 100644 index 0000000000000..367c995c6f7d3 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms.vendor; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.telephony.mbms.DownloadRequest; +import android.telephony.mbms.MbmsDownloadReceiver; + +import java.io.File; +import java.util.List; + +/** + * @hide + * TODO: future systemapi + */ +public class VendorIntents { + + /** + * The MBMS middleware should send this when a download of single file has completed or + * failed. Mandatory extras are + * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT} + * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO} + * {@link #EXTRA_REQUEST} + * {@link #EXTRA_TEMP_LIST} + * {@link #EXTRA_FINAL_URI} + */ + public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = + "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL"; + + /** + * The MBMS middleware should send this when it wishes to request {@code content://} URIs to + * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory + * extras are + * {@link #EXTRA_REQUEST} + * + * Optional extras are + * {@link #EXTRA_FD_COUNT} (0 if not present) + * {@link #EXTRA_PAUSED_LIST} (empty if not present) + */ + public static final String ACTION_FILE_DESCRIPTOR_REQUEST = + "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST"; + + /** + * The MBMS middleware should send this when it wishes to clean up temp files in the app's + * filesystem. Mandatory extras are: + * {@link #EXTRA_TEMP_FILES_IN_USE} + */ + public static final String ACTION_CLEANUP = + "android.telephony.mbms.action.CLEANUP"; + + /** + * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this + * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp + * files will be deleted upon receipt of the intent. + * May be null. + */ + public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST"; + + /** + * Extra containing an integer indicating the number of temp files requested. + */ + public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT"; + + /** + * Extra containing a list of {@link Uri}s that the middleware is requesting access to via + * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s + * should have scheme {@code file://}. + */ + public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST"; + + /** + * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the + * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant + * to be used for new file downloads. + */ + public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST"; + + /** + * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the + * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These + * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide + * access to previously paused downloads. + */ + public static final String EXTRA_PAUSED_URI_LIST = + "android.telephony.mbms.extra.PAUSED_URI_LIST"; + + /** + * Extra containing a string that points to the middleware's knowledge of where the temp file + * root for the app is. The path should be a canonical path as returned by + * {@link File#getCanonicalPath()} + */ + public static final String EXTRA_TEMP_FILE_ROOT = + "android.telephony.mbms.extra.TEMP_FILE_ROOT"; + + /** + * Extra containing a list of {@link Uri}s indicating temp files which the middleware is + * still using. + */ + public static final String EXTRA_TEMP_FILES_IN_USE = + "android.telephony.mbms.extra.TEMP_FILES_IN_USE"; + + /** + * Extra containing the {@link DownloadRequest} for which the download result or file + * descriptor request is for. Must not be null. + */ + public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST"; + + /** + * Extra containing a single {@link Uri} indicating the path to the temp file in which the + * decoded downloaded file resides. Must not be null. + */ + public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI"; + + /** + * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by + * file-descriptor requests and cleanup requests to specify which service they want to + * request temp files or clean up temp files for, respectively. + */ + public static final String EXTRA_SERVICE_INFO = + "android.telephony.mbms.extra.SERVICE_INFO"; + + /** + * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that + * the various intents from the middleware should be targeted towards. + * @param uid The uid of the frontend app. + * @return The component name of the receiver that the middleware should send its intents to, + * or null if the app didn't declare it in the manifest. + */ + public static ComponentName getAppReceiverFromUid(Context context, int uid) { + String[] packageNames = context.getPackageManager().getPackagesForUid(uid); + if (packageNames == null) { + return null; + } + + for (String packageName : packageNames) { + ComponentName candidate = new ComponentName(packageName, + MbmsDownloadReceiver.class.getCanonicalName()); + Intent queryIntent = new Intent(); + queryIntent.setComponent(candidate); + List receivers = + context.getPackageManager().queryBroadcastReceivers(queryIntent, 0); + if (receivers != null && receivers.size() > 0) { + return candidate; + } + } + return null; + } +}