Loading telephony/java/android/telephony/MbmsDownloadManager.java +222 −71 Original line number Diff line number Diff line Loading @@ -16,10 +16,13 @@ package android.telephony; import android.annotation.NonNull; 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; Loading @@ -27,13 +30,18 @@ import android.telephony.mbms.IDownloadCallback; import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadStatus; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsTempFileProvider; import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.vendor.IMbmsDownloadService; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; Loading @@ -47,7 +55,7 @@ public class MbmsDownloadManager { * The MBMS middleware should send this when a download of single file has completed or * failed. Mandatory extras are * {@link #EXTRA_RESULT} * {@link #EXTRA_INFO} * {@link #EXTRA_FILE_INFO} * {@link #EXTRA_REQUEST} * {@link #EXTRA_TEMP_LIST} * {@link #EXTRA_FINAL_URI} Loading Loading @@ -93,7 +101,7 @@ public class MbmsDownloadManager { * is for. Must not be null. * TODO: future systemapi (here and and all extras) except the two for the app intent */ public static final String EXTRA_INFO = "android.telephony.mbms.extra.INFO"; 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 Loading Loading @@ -143,6 +151,14 @@ public class MbmsDownloadManager { 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. Loading @@ -165,12 +181,10 @@ public class MbmsDownloadManager { public static final int RESULT_EXPIRED = 3; // TODO - more results! private static final long BIND_TIMEOUT_MS = 3000; private final Context mContext; private int mSubId = INVALID_SUBSCRIPTION_ID; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; private IMbmsDownloadService mService; private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null); private final IMbmsDownloadManagerCallback mCallback; private final String mDownloadAppName; Loading @@ -179,116 +193,221 @@ public class MbmsDownloadManager { mContext = context; mCallback = callback; mDownloadAppName = downloadAppName; mSubId = subId; mSubscriptionId = subId; } /** * Create a new MbmsDownloadManager using the system default data subscription ID. * * Note that this call will bind a remote service and that may take a bit. This * may throw an Illegal ArgumentException or RemoteException. * See {@link #create(Context, IMbmsDownloadManagerCallback, String, int)} * * @hide */ public static MbmsDownloadManager createManager(Context context, public static MbmsDownloadManager create(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName) throws MbmsException { MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName, return create(context, listener, downloadAppName, SubscriptionManager.getDefaultSubscriptionId()); mdm.bindAndInitialize(); return mdm; } /** * Create a new MbmsDownloadManager using the given subscription ID. * * Note that this call will bind a remote service and that may take a bit. This * may throw an Illegal ArgumentException or RemoteException. * 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. * 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}. * * @param context The instance of {@link Context} to use * @param listener A callback to get asynchronous error messages and file service updates. * @param downloadAppName The app name, as negotiated with the eMBMS provider * @param subscriptionId The data subscription ID to use * @hide */ public static MbmsDownloadManager createManager(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName, int subId) public static MbmsDownloadManager create(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName, int subscriptionId) throws MbmsException { MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName, subId); subscriptionId); mdm.bindAndInitialize(); return mdm; } private void bindAndInitialize() throws MbmsException { // TODO: fold binding for download and streaming into a common utils class. final CountDownLatch latch = new CountDownLatch(1); ServiceConnection bindListener = new ServiceConnection() { MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IMbmsDownloadService.Stub.asInterface(service); latch.countDown(); IMbmsDownloadService downloadService = IMbmsDownloadService.Stub.asInterface(service); try { downloadService.initialize( mDownloadAppName, mSubscriptionId, mCallback); } catch (RemoteException e) { Log.e(LOG_TAG, "Service died before initialization"); return; } mService.set(downloadService); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mService.set(null); } }; Intent bindIntent = new Intent(); bindIntent.setComponent(MbmsUtils.toComponentName( MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION))); // Kick off the binding, and synchronously wait until binding is complete mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE); MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); // TODO: initialize }); } /** * Gets the list of files published for download. * They may occur at times far in the future. * servicesClasses lets the app filter on types of files and is opaque data between * the app and the carrier * 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)} * * Multiple calls replace trhe list of serviceClasses of interest. * The serviceClasses argument lets the app filter on types of programming and is opaque data * negotiated beforehand between the app and the carrier. * * May throw an IllegalArgumentException or RemoteException. * Multiple calls replace the list of serviceClasses of interest. * * Synchronous responses include * <li>SUCCESS</li> * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li> * This may throw an {@link MbmsException} containing one of the following errors: * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} * {@link MbmsException#ERROR_SERVICE_LOST} * * Asynchronous errors through the listener include any of the errors except * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li> * <li>ERROR_MSDC_INVALID_SERVICE_ID</li> * <li>ERROR_MSDC_END_OF_SESSION</li> * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)} * callback can include any of the errors except: * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE} * {@link MbmsException#ERROR_END_OF_SESSION} */ public int getFileServices(List<String> serviceClasses) { return 0; public void getFileServices(List<String> classList) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } try { int returnCode = downloadService.getFileServices( mDownloadAppName, mSubscriptionId, classList); if (returnCode != MbmsException.SUCCESS) { throw new MbmsException(returnCode); } } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } } /** * Sets the temp file root for downloads. * All temp files created for the middleware to write to will be contained in the specified * directory. Applications that wish to specify a location only need to call this method once * as long their data is persisted in storage -- the argument will be stored both in a * 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 * 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}. * * This method may not be called while any download requests are still active. If this is * the case, an {@link MbmsException} will be thrown with code * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} * * The {@link File} supplied as a root temp file directory must already exist. If not, an * {@link IllegalArgumentException} will be thrown. * @param tempFileRootDirectory A directory to place temp files in. */ public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } if (!tempFileRootDirectory.exists()) { throw new IllegalArgumentException("Provided directory does not exist"); } if (!tempFileRootDirectory.isDirectory()) { throw new IllegalArgumentException("Provided File is not a directory"); } String filePath; try { filePath = tempFileRootDirectory.getCanonicalPath(); } catch (IOException e) { throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e); } try { int result = downloadService.setTempFileRootDirectory( mDownloadAppName, mSubscriptionId, filePath); if (result != MbmsException.SUCCESS) { throw new MbmsException(result); } } catch (RemoteException e) { mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } SharedPreferences prefs = mContext.getSharedPreferences( MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0); prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply(); } /** * Requests a future download. * returns a token which may be used to cancel a download. * Requests a download of a file that is available via multicast. * * downloadListener is an optional callback object which can be used to get progress reports * of a currently occuring download. Note this can only run while the calling app * is running, so future downloads will simply result in resultIntents being sent * for completed or errored-out downloads. A NULL indicates no callbacks are needed. * * May throw an IllegalArgumentException or RemoteExcpetion. * May throw an {@link IllegalArgumentException} * * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed, * this method will create a directory at the default location defined at * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp * file root directory. * * Asynchronous errors through the listener include any of the errors * * @param request The request that specifies what should be downloaded * @param callback Optional callback that will provide progress updates if the app is running. */ public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) { public void download(DownloadRequest request, IDownloadCallback callback) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } // Check to see whether the app's set a temp root dir yet, and set it if not. SharedPreferences prefs = mContext.getSharedPreferences( MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0); if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) { File tempRootDirectory = new File(mContext.getFilesDir(), MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY); tempRootDirectory.mkdirs(); setTempFileRootDirectory(tempRootDirectory); } request.setAppName(mDownloadAppName); // Check if the request is a multipart download. If so, validate that the destination is // a directory that exists. // TODO: figure out what qualifies a request as a multipart download request. if (request.getSourceUri().getLastPathSegment() != null && request.getSourceUri().getLastPathSegment().contains("*")) { File toFile = new File(request.getDestinationUri().getSchemeSpecificPart()); if (!toFile.isDirectory()) { throw new IllegalArgumentException("Multipart download must specify valid " + "destination directory."); } } // TODO: check to make sure destination is clear // TODO: write download request token try { mService.download(request, listener); downloadService.download(request, callback); } catch (RemoteException e) { mService = null; mService.set(null); } return request; } /** Loading Loading @@ -355,13 +474,45 @@ public class MbmsDownloadManager { return 0; } /** * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that * the various intents from the middleware should be targeted towards. * @param uid The uid of the frontend app. * @return The component name of the receiver that the middleware should send its intents to, * or null if the app didn't declare it in the manifest. * * @hide * future systemapi */ public static ComponentName getAppReceiverFromUid(Context context, int uid) { String[] packageNames = context.getPackageManager().getPackagesForUid(uid); if (packageNames == null) { return null; } for (String packageName : packageNames) { ComponentName candidate = new ComponentName(packageName, MbmsDownloadReceiver.class.getCanonicalName()); Intent queryIntent = new Intent(); queryIntent.setComponent(candidate); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(queryIntent, 0); if (receivers != null && receivers.size() > 0) { return candidate; } } return null; } public void dispose() { try { if (mService != null) { mService.dispose(mDownloadAppName, mSubId); } else { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { Log.i(LOG_TAG, "Service already dead"); return; } downloadService.dispose(mDownloadAppName, mSubscriptionId); mService.set(null); } catch (RemoteException e) { // Ignore Log.i(LOG_TAG, "Remote exception while disposing of service"); Loading telephony/java/android/telephony/MbmsStreamingManager.java +39 −105 File changed.Preview size limit exceeded, changes collapsed. Show changes telephony/java/android/telephony/mbms/DownloadRequest.java +12 −12 Original line number Diff line number Diff line Loading @@ -35,9 +35,8 @@ public class DownloadRequest implements Parcelable { private FileServiceInfo serviceInfo; private Uri source; private Uri dest; private int sub; private int subscriptionId; private String appIntent; private String appName; // not the Android app Name, the embms app Name public Builder setId(int id) { this.id = id; Loading @@ -59,8 +58,8 @@ public class DownloadRequest implements Parcelable { return this; } public Builder setSub(int sub) { this.sub = sub; public Builder setSubscriptionId(int sub) { this.subscriptionId = sub; return this; } Loading @@ -70,7 +69,8 @@ public class DownloadRequest implements Parcelable { } public DownloadRequest build() { return new DownloadRequest(id, serviceInfo, source, dest, sub, appIntent, appName); return new DownloadRequest(id, serviceInfo, source, dest, subscriptionId, appIntent, null); } } Loading @@ -78,7 +78,7 @@ public class DownloadRequest implements Parcelable { private final FileServiceInfo fileServiceInfo; private final Uri sourceUri; private final Uri destinationUri; private final int subId; private final int subscriptionId; private final String serializedResultIntentForApp; private String appName; // not the Android app Name, the embms app name Loading @@ -89,7 +89,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = serviceInfo; sourceUri = source; destinationUri = dest; subId = sub; subscriptionId = sub; serializedResultIntentForApp = appIntent; appName = name; } Loading @@ -103,7 +103,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = dr.fileServiceInfo; sourceUri = dr.sourceUri; destinationUri = dr.destinationUri; subId = dr.subId; subscriptionId = dr.subscriptionId; serializedResultIntentForApp = dr.serializedResultIntentForApp; appName = dr.appName; } Loading @@ -113,7 +113,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = in.readParcelable(getClass().getClassLoader()); sourceUri = in.readParcelable(getClass().getClassLoader()); destinationUri = in.readParcelable(getClass().getClassLoader()); subId = in.readInt(); subscriptionId = in.readInt(); serializedResultIntentForApp = in.readString(); appName = in.readString(); } Loading @@ -127,7 +127,7 @@ public class DownloadRequest implements Parcelable { out.writeParcelable(fileServiceInfo, flags); out.writeParcelable(sourceUri, flags); out.writeParcelable(destinationUri, flags); out.writeInt(subId); out.writeInt(subscriptionId); out.writeString(serializedResultIntentForApp); out.writeString(appName); } Loading @@ -148,8 +148,8 @@ public class DownloadRequest implements Parcelable { return destinationUri; } public int getSubId() { return subId; public int getSubscriptionId() { return subscriptionId; } public Intent getIntentForApp() { Loading telephony/java/android/telephony/mbms/FileInfo.java +27 −11 Original line number Diff line number Diff line Loading @@ -31,29 +31,22 @@ public class FileInfo implements Parcelable { * This is used internally but is also one of the few pieces of data about the content that is * exposed and may be needed for disambiguation by the application. */ final Uri uri; private final Uri uri; /** * The mime type of the content. */ final String mimeType; private final String mimeType; /** * The size of the file in bytes. */ final long size; private final long size; /** * The MD5 hash of the file. */ final byte md5Hash[]; /** * Gets the parent service for this file. */ public FileServiceInfo getFileServiceInfo() { return null; } private final byte md5Hash[]; public static final Parcelable.Creator<FileInfo> CREATOR = new Parcelable.Creator<FileInfo>() { Loading @@ -68,6 +61,13 @@ public class FileInfo implements Parcelable { } }; public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) { this.uri = uri; this.mimeType = mimeType; this.size = size; this.md5Hash = md5Hash; } private FileInfo(Parcel in) { uri = in.readParcelable(null); mimeType = in.readString(); Loading @@ -90,4 +90,20 @@ public class FileInfo implements Parcelable { public int describeContents() { return 0; } public Uri getUri() { return uri; } public String getMimeType() { return mimeType; } public long getSize() { return size; } public byte[] getMd5Hash() { return md5Hash; } } telephony/java/android/telephony/mbms/FileServiceInfo.java +7 −2 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import java.util.Map; * @hide */ public class FileServiceInfo extends ServiceInfo implements Parcelable { public List<FileInfo> files; private final List<FileInfo> files; public FileServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales, String newServiceId, Date start, Date end, List<FileInfo> newFiles) { super(newNames, newClassName, newLocales, newServiceId, start, end); files = new ArrayList(newFiles); files = new ArrayList<>(newFiles); } public static final Parcelable.Creator<FileServiceInfo> CREATOR = Loading Loading @@ -68,4 +68,9 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable { public int describeContents() { return 0; } public List<FileInfo> getFiles() { return files; } } Loading
telephony/java/android/telephony/MbmsDownloadManager.java +222 −71 Original line number Diff line number Diff line Loading @@ -16,10 +16,13 @@ package android.telephony; import android.annotation.NonNull; 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; Loading @@ -27,13 +30,18 @@ import android.telephony.mbms.IDownloadCallback; import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadStatus; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsTempFileProvider; import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.vendor.IMbmsDownloadService; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; Loading @@ -47,7 +55,7 @@ public class MbmsDownloadManager { * The MBMS middleware should send this when a download of single file has completed or * failed. Mandatory extras are * {@link #EXTRA_RESULT} * {@link #EXTRA_INFO} * {@link #EXTRA_FILE_INFO} * {@link #EXTRA_REQUEST} * {@link #EXTRA_TEMP_LIST} * {@link #EXTRA_FINAL_URI} Loading Loading @@ -93,7 +101,7 @@ public class MbmsDownloadManager { * is for. Must not be null. * TODO: future systemapi (here and and all extras) except the two for the app intent */ public static final String EXTRA_INFO = "android.telephony.mbms.extra.INFO"; 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 Loading Loading @@ -143,6 +151,14 @@ public class MbmsDownloadManager { 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. Loading @@ -165,12 +181,10 @@ public class MbmsDownloadManager { public static final int RESULT_EXPIRED = 3; // TODO - more results! private static final long BIND_TIMEOUT_MS = 3000; private final Context mContext; private int mSubId = INVALID_SUBSCRIPTION_ID; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; private IMbmsDownloadService mService; private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null); private final IMbmsDownloadManagerCallback mCallback; private final String mDownloadAppName; Loading @@ -179,116 +193,221 @@ public class MbmsDownloadManager { mContext = context; mCallback = callback; mDownloadAppName = downloadAppName; mSubId = subId; mSubscriptionId = subId; } /** * Create a new MbmsDownloadManager using the system default data subscription ID. * * Note that this call will bind a remote service and that may take a bit. This * may throw an Illegal ArgumentException or RemoteException. * See {@link #create(Context, IMbmsDownloadManagerCallback, String, int)} * * @hide */ public static MbmsDownloadManager createManager(Context context, public static MbmsDownloadManager create(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName) throws MbmsException { MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName, return create(context, listener, downloadAppName, SubscriptionManager.getDefaultSubscriptionId()); mdm.bindAndInitialize(); return mdm; } /** * Create a new MbmsDownloadManager using the given subscription ID. * * Note that this call will bind a remote service and that may take a bit. This * may throw an Illegal ArgumentException or RemoteException. * 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. * 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}. * * @param context The instance of {@link Context} to use * @param listener A callback to get asynchronous error messages and file service updates. * @param downloadAppName The app name, as negotiated with the eMBMS provider * @param subscriptionId The data subscription ID to use * @hide */ public static MbmsDownloadManager createManager(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName, int subId) public static MbmsDownloadManager create(Context context, IMbmsDownloadManagerCallback listener, String downloadAppName, int subscriptionId) throws MbmsException { MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName, subId); subscriptionId); mdm.bindAndInitialize(); return mdm; } private void bindAndInitialize() throws MbmsException { // TODO: fold binding for download and streaming into a common utils class. final CountDownLatch latch = new CountDownLatch(1); ServiceConnection bindListener = new ServiceConnection() { MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IMbmsDownloadService.Stub.asInterface(service); latch.countDown(); IMbmsDownloadService downloadService = IMbmsDownloadService.Stub.asInterface(service); try { downloadService.initialize( mDownloadAppName, mSubscriptionId, mCallback); } catch (RemoteException e) { Log.e(LOG_TAG, "Service died before initialization"); return; } mService.set(downloadService); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mService.set(null); } }; Intent bindIntent = new Intent(); bindIntent.setComponent(MbmsUtils.toComponentName( MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION))); // Kick off the binding, and synchronously wait until binding is complete mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE); MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); // TODO: initialize }); } /** * Gets the list of files published for download. * They may occur at times far in the future. * servicesClasses lets the app filter on types of files and is opaque data between * the app and the carrier * 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)} * * Multiple calls replace trhe list of serviceClasses of interest. * The serviceClasses argument lets the app filter on types of programming and is opaque data * negotiated beforehand between the app and the carrier. * * May throw an IllegalArgumentException or RemoteException. * Multiple calls replace the list of serviceClasses of interest. * * Synchronous responses include * <li>SUCCESS</li> * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li> * This may throw an {@link MbmsException} containing one of the following errors: * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} * {@link MbmsException#ERROR_SERVICE_LOST} * * Asynchronous errors through the listener include any of the errors except * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li> * <li>ERROR_MSDC_INVALID_SERVICE_ID</li> * <li>ERROR_MSDC_END_OF_SESSION</li> * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)} * callback can include any of the errors except: * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE} * {@link MbmsException#ERROR_END_OF_SESSION} */ public int getFileServices(List<String> serviceClasses) { return 0; public void getFileServices(List<String> classList) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } try { int returnCode = downloadService.getFileServices( mDownloadAppName, mSubscriptionId, classList); if (returnCode != MbmsException.SUCCESS) { throw new MbmsException(returnCode); } } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } } /** * Sets the temp file root for downloads. * All temp files created for the middleware to write to will be contained in the specified * directory. Applications that wish to specify a location only need to call this method once * as long their data is persisted in storage -- the argument will be stored both in a * 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 * 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}. * * This method may not be called while any download requests are still active. If this is * the case, an {@link MbmsException} will be thrown with code * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} * * The {@link File} supplied as a root temp file directory must already exist. If not, an * {@link IllegalArgumentException} will be thrown. * @param tempFileRootDirectory A directory to place temp files in. */ public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } if (!tempFileRootDirectory.exists()) { throw new IllegalArgumentException("Provided directory does not exist"); } if (!tempFileRootDirectory.isDirectory()) { throw new IllegalArgumentException("Provided File is not a directory"); } String filePath; try { filePath = tempFileRootDirectory.getCanonicalPath(); } catch (IOException e) { throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e); } try { int result = downloadService.setTempFileRootDirectory( mDownloadAppName, mSubscriptionId, filePath); if (result != MbmsException.SUCCESS) { throw new MbmsException(result); } } catch (RemoteException e) { mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } SharedPreferences prefs = mContext.getSharedPreferences( MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0); prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply(); } /** * Requests a future download. * returns a token which may be used to cancel a download. * Requests a download of a file that is available via multicast. * * downloadListener is an optional callback object which can be used to get progress reports * of a currently occuring download. Note this can only run while the calling app * is running, so future downloads will simply result in resultIntents being sent * for completed or errored-out downloads. A NULL indicates no callbacks are needed. * * May throw an IllegalArgumentException or RemoteExcpetion. * May throw an {@link IllegalArgumentException} * * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed, * this method will create a directory at the default location defined at * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp * file root directory. * * Asynchronous errors through the listener include any of the errors * * @param request The request that specifies what should be downloaded * @param callback Optional callback that will provide progress updates if the app is running. */ public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) { public void download(DownloadRequest request, IDownloadCallback callback) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } // Check to see whether the app's set a temp root dir yet, and set it if not. SharedPreferences prefs = mContext.getSharedPreferences( MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0); if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) { File tempRootDirectory = new File(mContext.getFilesDir(), MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY); tempRootDirectory.mkdirs(); setTempFileRootDirectory(tempRootDirectory); } request.setAppName(mDownloadAppName); // Check if the request is a multipart download. If so, validate that the destination is // a directory that exists. // TODO: figure out what qualifies a request as a multipart download request. if (request.getSourceUri().getLastPathSegment() != null && request.getSourceUri().getLastPathSegment().contains("*")) { File toFile = new File(request.getDestinationUri().getSchemeSpecificPart()); if (!toFile.isDirectory()) { throw new IllegalArgumentException("Multipart download must specify valid " + "destination directory."); } } // TODO: check to make sure destination is clear // TODO: write download request token try { mService.download(request, listener); downloadService.download(request, callback); } catch (RemoteException e) { mService = null; mService.set(null); } return request; } /** Loading Loading @@ -355,13 +474,45 @@ public class MbmsDownloadManager { return 0; } /** * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that * the various intents from the middleware should be targeted towards. * @param uid The uid of the frontend app. * @return The component name of the receiver that the middleware should send its intents to, * or null if the app didn't declare it in the manifest. * * @hide * future systemapi */ public static ComponentName getAppReceiverFromUid(Context context, int uid) { String[] packageNames = context.getPackageManager().getPackagesForUid(uid); if (packageNames == null) { return null; } for (String packageName : packageNames) { ComponentName candidate = new ComponentName(packageName, MbmsDownloadReceiver.class.getCanonicalName()); Intent queryIntent = new Intent(); queryIntent.setComponent(candidate); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(queryIntent, 0); if (receivers != null && receivers.size() > 0) { return candidate; } } return null; } public void dispose() { try { if (mService != null) { mService.dispose(mDownloadAppName, mSubId); } else { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { Log.i(LOG_TAG, "Service already dead"); return; } downloadService.dispose(mDownloadAppName, mSubscriptionId); mService.set(null); } catch (RemoteException e) { // Ignore Log.i(LOG_TAG, "Remote exception while disposing of service"); Loading
telephony/java/android/telephony/MbmsStreamingManager.java +39 −105 File changed.Preview size limit exceeded, changes collapsed. Show changes
telephony/java/android/telephony/mbms/DownloadRequest.java +12 −12 Original line number Diff line number Diff line Loading @@ -35,9 +35,8 @@ public class DownloadRequest implements Parcelable { private FileServiceInfo serviceInfo; private Uri source; private Uri dest; private int sub; private int subscriptionId; private String appIntent; private String appName; // not the Android app Name, the embms app Name public Builder setId(int id) { this.id = id; Loading @@ -59,8 +58,8 @@ public class DownloadRequest implements Parcelable { return this; } public Builder setSub(int sub) { this.sub = sub; public Builder setSubscriptionId(int sub) { this.subscriptionId = sub; return this; } Loading @@ -70,7 +69,8 @@ public class DownloadRequest implements Parcelable { } public DownloadRequest build() { return new DownloadRequest(id, serviceInfo, source, dest, sub, appIntent, appName); return new DownloadRequest(id, serviceInfo, source, dest, subscriptionId, appIntent, null); } } Loading @@ -78,7 +78,7 @@ public class DownloadRequest implements Parcelable { private final FileServiceInfo fileServiceInfo; private final Uri sourceUri; private final Uri destinationUri; private final int subId; private final int subscriptionId; private final String serializedResultIntentForApp; private String appName; // not the Android app Name, the embms app name Loading @@ -89,7 +89,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = serviceInfo; sourceUri = source; destinationUri = dest; subId = sub; subscriptionId = sub; serializedResultIntentForApp = appIntent; appName = name; } Loading @@ -103,7 +103,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = dr.fileServiceInfo; sourceUri = dr.sourceUri; destinationUri = dr.destinationUri; subId = dr.subId; subscriptionId = dr.subscriptionId; serializedResultIntentForApp = dr.serializedResultIntentForApp; appName = dr.appName; } Loading @@ -113,7 +113,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = in.readParcelable(getClass().getClassLoader()); sourceUri = in.readParcelable(getClass().getClassLoader()); destinationUri = in.readParcelable(getClass().getClassLoader()); subId = in.readInt(); subscriptionId = in.readInt(); serializedResultIntentForApp = in.readString(); appName = in.readString(); } Loading @@ -127,7 +127,7 @@ public class DownloadRequest implements Parcelable { out.writeParcelable(fileServiceInfo, flags); out.writeParcelable(sourceUri, flags); out.writeParcelable(destinationUri, flags); out.writeInt(subId); out.writeInt(subscriptionId); out.writeString(serializedResultIntentForApp); out.writeString(appName); } Loading @@ -148,8 +148,8 @@ public class DownloadRequest implements Parcelable { return destinationUri; } public int getSubId() { return subId; public int getSubscriptionId() { return subscriptionId; } public Intent getIntentForApp() { Loading
telephony/java/android/telephony/mbms/FileInfo.java +27 −11 Original line number Diff line number Diff line Loading @@ -31,29 +31,22 @@ public class FileInfo implements Parcelable { * This is used internally but is also one of the few pieces of data about the content that is * exposed and may be needed for disambiguation by the application. */ final Uri uri; private final Uri uri; /** * The mime type of the content. */ final String mimeType; private final String mimeType; /** * The size of the file in bytes. */ final long size; private final long size; /** * The MD5 hash of the file. */ final byte md5Hash[]; /** * Gets the parent service for this file. */ public FileServiceInfo getFileServiceInfo() { return null; } private final byte md5Hash[]; public static final Parcelable.Creator<FileInfo> CREATOR = new Parcelable.Creator<FileInfo>() { Loading @@ -68,6 +61,13 @@ public class FileInfo implements Parcelable { } }; public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) { this.uri = uri; this.mimeType = mimeType; this.size = size; this.md5Hash = md5Hash; } private FileInfo(Parcel in) { uri = in.readParcelable(null); mimeType = in.readString(); Loading @@ -90,4 +90,20 @@ public class FileInfo implements Parcelable { public int describeContents() { return 0; } public Uri getUri() { return uri; } public String getMimeType() { return mimeType; } public long getSize() { return size; } public byte[] getMd5Hash() { return md5Hash; } }
telephony/java/android/telephony/mbms/FileServiceInfo.java +7 −2 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import java.util.Map; * @hide */ public class FileServiceInfo extends ServiceInfo implements Parcelable { public List<FileInfo> files; private final List<FileInfo> files; public FileServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales, String newServiceId, Date start, Date end, List<FileInfo> newFiles) { super(newNames, newClassName, newLocales, newServiceId, start, end); files = new ArrayList(newFiles); files = new ArrayList<>(newFiles); } public static final Parcelable.Creator<FileServiceInfo> CREATOR = Loading Loading @@ -68,4 +68,9 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable { public int describeContents() { return 0; } public List<FileInfo> getFiles() { return files; } }