diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java index c8c6d014a059a..79ee37a168d44 100644 --- a/telephony/java/android/telephony/MbmsDownloadManager.java +++ b/telephony/java/android/telephony/MbmsDownloadManager.java @@ -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; @@ -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; @@ -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} @@ -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 @@ -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. @@ -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 mService = new AtomicReference<>(null); private final IMbmsDownloadManagerCallback mCallback; private final String mDownloadAppName; @@ -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() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mService = IMbmsDownloadService.Stub.asInterface(service); - latch.countDown(); - } + MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + 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; - } - }; - - 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 + @Override + public void onServiceDisconnected(ComponentName name) { + mService.set(null); + } + }); } /** - * 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 - *
  • SUCCESS
  • - *
  • ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED
  • + * 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 - *
  • ERROR_MSDC_UNABLE_TO_)START_SERVICE
  • - *
  • ERROR_MSDC_INVALID_SERVICE_ID
  • - *
  • ERROR_MSDC_END_OF_SESSION
  • + * 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 serviceClasses) { - return 0; + public void getFileServices(List 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) { - request.setAppName(mDownloadAppName); - try { - mService.download(request, listener); - } catch (RemoteException e) { - mService = null; + 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 { + downloadService.download(request, callback); + } catch (RemoteException e) { + mService.set(null); } - return request; } /** @@ -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 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"); diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java index f68e2439971f8..af7f333390d83 100644 --- a/telephony/java/android/telephony/MbmsStreamingManager.java +++ b/telephony/java/android/telephony/MbmsStreamingManager.java @@ -18,11 +18,7 @@ package android.telephony; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.DeadObjectException; import android.os.IBinder; import android.os.RemoteException; import android.telephony.mbms.MbmsException; @@ -34,56 +30,18 @@ import android.telephony.mbms.StreamingServiceInfo; import android.telephony.mbms.vendor.IMbmsStreamingService; import android.util.Log; -import java.util.LinkedList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; /** @hide */ public class MbmsStreamingManager { - private interface ServiceListener { - void onServiceConnected(); - void onServiceDisconnected(); - } - private static final String LOG_TAG = "MbmsStreamingManager"; public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; - private static final boolean DEBUG = true; - private static final int BIND_TIMEOUT_MS = 3000; - - private IMbmsStreamingService mService; - private ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (service != null) { - Log.i(LOG_TAG, String.format("Connected to service %s", name)); - synchronized (MbmsStreamingManager.this) { - mService = IMbmsStreamingService.Stub.asInterface(service); - for (ServiceListener l : mServiceListeners) { - l.onServiceConnected(); - } - } - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - Log.i(LOG_TAG, String.format("Disconnected from service %s", name)); - synchronized (MbmsStreamingManager.this) { - mService = null; - for (ServiceListener l : mServiceListeners) { - l.onServiceDisconnected(); - } - } - } - }; - - private List mServiceListeners = new LinkedList<>(); - + private AtomicReference mService = new AtomicReference<>(null); private MbmsStreamingManagerCallback mCallbackToApp; private final String mAppName; @@ -128,28 +86,26 @@ public class MbmsStreamingManager { public static MbmsStreamingManager create(Context context, MbmsStreamingManagerCallback listener, String streamingAppName) throws MbmsException { - int subId = SubscriptionManager.getDefaultSubscriptionId(); - MbmsStreamingManager manager = new MbmsStreamingManager(context, listener, - streamingAppName, subId); - manager.bindAndInitialize(); - return manager; + return create(context, listener, streamingAppName, + SubscriptionManager.getDefaultSubscriptionId()); } /** * Terminates this instance, ending calls to the registered listener. Also terminates * any streaming services spawned from this instance. */ - public synchronized void dispose() { - if (mService == null) { + public void dispose() { + IMbmsStreamingService streamingService = mService.get(); + if (streamingService == null) { // Ignore and return, assume already disposed. return; } try { - mService.dispose(mAppName, mSubscriptionId); + streamingService.dispose(mAppName, mSubscriptionId); } catch (RemoteException e) { // Ignore for now } - mService = null; + mService.set(null); } /** @@ -171,17 +127,19 @@ public class MbmsStreamingManager { * {@link MbmsException#ERROR_END_OF_SESSION} */ public void getStreamingServices(List classList) throws MbmsException { - if (mService == null) { + IMbmsStreamingService streamingService = mService.get(); + if (streamingService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } try { - int returnCode = mService.getStreamingServices(mAppName, mSubscriptionId, classList); + int returnCode = streamingService.getStreamingServices( + mAppName, mSubscriptionId, classList); if (returnCode != MbmsException.SUCCESS) { throw new MbmsException(returnCode); } } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); - mService = null; + mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } } @@ -190,7 +148,7 @@ public class MbmsStreamingManager { * Starts streaming a requested service, reporting status to the indicated listener. * 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#streamStateChanged(int)}. + * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int)} * * May throw an {@link MbmsException} containing any of the following error codes: * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} @@ -203,71 +161,47 @@ public class MbmsStreamingManager { */ public StreamingService startStreaming(StreamingServiceInfo serviceInfo, StreamingServiceCallback listener) throws MbmsException { - if (mService == null) { + IMbmsStreamingService streamingService = mService.get(); + if (streamingService == null) { throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); } try { - int returnCode = mService.startStreaming( + int returnCode = streamingService.startStreaming( mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener); if (returnCode != MbmsException.SUCCESS) { throw new MbmsException(returnCode); } } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); - mService = null; + mService.set(null); throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } return new StreamingService( - mAppName, mSubscriptionId, mService, serviceInfo, listener); + mAppName, mSubscriptionId, streamingService, serviceInfo, listener); } private void bindAndInitialize() throws MbmsException { - // Kick off the binding, and synchronously wait until binding is complete - final CountDownLatch latch = new CountDownLatch(1); - ServiceListener bindListener = new ServiceListener() { - @Override - public void onServiceConnected() { - latch.countDown(); - } + MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION, + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + IMbmsStreamingService streamingService = + IMbmsStreamingService.Stub.asInterface(service); + try { + streamingService.initialize(mCallbackToApp, mAppName, mSubscriptionId); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Service died before initialization"); + return; + } + mService.set(null); + } - @Override - public void onServiceDisconnected() { - } - }; - - synchronized (this) { - mServiceListeners.add(bindListener); - } - - Intent bindIntent = new Intent(); - bindIntent.setComponent(MbmsUtils.toComponentName( - MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_STREAMING_SERVICE_ACTION))); - - mContext.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); - - MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); - - // Remove the listener and call the initialization method through the interface. - synchronized (this) { - mServiceListeners.remove(bindListener); - - if (mService == null) { - throw new MbmsException(MbmsException.ERROR_BIND_TIMEOUT_OR_FAILURE); - } - - try { - int returnCode = mService.initialize(mCallbackToApp, mAppName, mSubscriptionId); - if (returnCode != MbmsException.SUCCESS) { - throw new MbmsException(returnCode); - } - } catch (RemoteException e) { - mService = null; - Log.e(LOG_TAG, "Service died before initialization"); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); - } - } + @Override + public void onServiceDisconnected(ComponentName name) { + mService.set(null); + } + }); } - } diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index f3ca05840da21..c561741cc80c6 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -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; @@ -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; } @@ -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); } } @@ -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 @@ -89,7 +89,7 @@ public class DownloadRequest implements Parcelable { fileServiceInfo = serviceInfo; sourceUri = source; destinationUri = dest; - subId = sub; + subscriptionId = sub; serializedResultIntentForApp = appIntent; appName = name; } @@ -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; } @@ -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(); } @@ -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); } @@ -148,8 +148,8 @@ public class DownloadRequest implements Parcelable { return destinationUri; } - public int getSubId() { - return subId; + public int getSubscriptionId() { + return subscriptionId; } public Intent getIntentForApp() { diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java index d3888bdd2c9ae..1b873938a3f20 100644 --- a/telephony/java/android/telephony/mbms/FileInfo.java +++ b/telephony/java/android/telephony/mbms/FileInfo.java @@ -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 CREATOR = new Parcelable.Creator() { @@ -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(); @@ -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; + } } diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java index 8e890fd580e37..6646dc8a56dfb 100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.java +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java @@ -30,13 +30,13 @@ import java.util.Map; * @hide */ public class FileServiceInfo extends ServiceInfo implements Parcelable { - public List files; + private final List files; public FileServiceInfo(Map newNames, String newClassName, List newLocales, String newServiceId, Date start, Date end, List newFiles) { super(newNames, newClassName, newLocales, newServiceId, start, end); - files = new ArrayList(newFiles); + files = new ArrayList<>(newFiles); } public static final Parcelable.Creator CREATOR = @@ -68,4 +68,9 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable { public int describeContents() { return 0; } + + public List getFiles() { + return files; + } + } diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl index 03227d0d9f5ab..ac2f202438966 100755 --- a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl @@ -24,19 +24,11 @@ import java.util.List; * The interface the clients top-level file download listener will satisfy. * @hide */ -interface IMbmsDownloadManagerCallback +oneway interface IMbmsDownloadManagerCallback { void error(int errorCode, String message); - /** - * Called to indicate published File Services have changed. - * - * This will only be called after the application has requested - * a list of file services and specified a service class list - * of interest AND the results of a subsequent getFileServices - * call with the same service class list would - * return different - * results. - */ void fileServicesUpdated(in List services); + + void middlewareReady(); } diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl index cbf0fca461f0d..8116a7f0b7c41 100755 --- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl @@ -24,30 +24,13 @@ import java.util.List; * The interface the clients top-level streaming listener will satisfy. * @hide */ -interface IMbmsStreamingManagerCallback +oneway interface IMbmsStreamingManagerCallback { void error(int errorCode, String message); - /** - * Called to indicate published Streaming Services have changed. - * - * This will only be called after the application has requested - * a list of streaming services and specified a service class list - * of interest AND the results of a subsequent getStreamServices - * call with the same service class list would - * return different - * results. - */ void streamingServicesUpdated(in List services); - /** - * Called to indicate the active Streaming Services have changed. - * - * This will be caused whenever a new service starts streaming or whenever - * MbmsStreamServiceManager.getActiveStreamingServices is called. - * - * @param services a list of StreamingServiceInfos. May be empty if - * there are no active StreamingServices - */ void activeStreamingServicesUpdated(in List services); + + void middlewareReady(); } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java index 16fafe415b0f3..5b22199bea1cd 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java @@ -48,4 +48,17 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St public void fileServicesUpdated(List services) { // default implementation empty } + + /** + * Called to indicate that the middleware has been initialized and is ready. + * + * Before this method is called, calling any method on an instance of + * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException} + * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} + * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY} + */ + @Override + public void middlewareReady() { + // default implementation empty + } } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java index c01ddaedbd889..b51c367deb36c 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java @@ -31,8 +31,8 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.UUID; /** @@ -54,6 +54,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { setResultCode(1 /* TODO: define error constants */); return; } + if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT), + MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) { + setResultCode(1 /* TODO: define error constants */); + return; + } if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { moveDownloadedFile(context, intent); @@ -74,7 +79,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring."); return false; } - if (!intent.hasExtra(MbmsDownloadManager.EXTRA_INFO)) { + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) { + Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring."); + return false; + } + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) { Log.w(LOG_TAG, "Download result did not include the associated file info. " + "Ignoring."); return false; @@ -90,6 +99,10 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { Log.w(LOG_TAG, "Temp file request not include the associated request. Ignoring."); return false; } + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) { + Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring."); + return false; + } return true; } @@ -121,12 +134,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { } String relativePath = calculateDestinationFileRelativePath(request, - (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_INFO)); + (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO)); - if (!moveTempFile(finalTempFile, destinationUri, relativePath)) { + Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath); + if (finalFileLocation == null) { Log.w(LOG_TAG, "Failed to move temp file to final destination"); + // TODO: how do we notify the app of this? setResultCode(1); } + intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation); context.sendBroadcast(intentForApp); setResultCode(0); @@ -226,7 +242,6 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { return null; } - private ArrayList generateUrisForPausedFiles(Context context, DownloadRequest request, List pausedFiles) { if (pausedFiles == null) { @@ -258,13 +273,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { private static String calculateDestinationFileRelativePath(DownloadRequest request, FileInfo info) { - // TODO: determine whether this is actually the path determination scheme we want to use - List filePathComponents = info.uri.getPathSegments(); + List filePathComponents = info.getUri().getPathSegments(); List requestPathComponents = request.getSourceUri().getPathSegments(); Iterator filePathIter = filePathComponents.iterator(); Iterator requestPathIter = requestPathComponents.iterator(); - LinkedList relativePathComponents = new LinkedList<>(); + StringBuilder pathBuilder = new StringBuilder(); + // Iterate through the segments of the carrier's URI to the file, along with the segments + // of the source URI specified in the download request. The relative path is calculated + // as the tail of the file's URI that does not match the path segments in the source URI. while (filePathIter.hasNext()) { String currFilePathComponent = filePathIter.next(); if (requestPathIter.hasNext()) { @@ -273,28 +290,44 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { continue; } } - relativePathComponents.add(currFilePathComponent); + pathBuilder.append(currFilePathComponent); + pathBuilder.append('/'); } - return String.join("/", relativePathComponents); + // remove the trailing slash + if (pathBuilder.length() > 0) { + pathBuilder.deleteCharAt(pathBuilder.length() - 1); + } + return pathBuilder.toString(); } - private static boolean moveTempFile(Uri fromPath, Uri toPath, String relativePath) { + /* + * Moves a tempfile located at fromPath to a new location at toPath. If + * toPath is a directory, the destination file will be located at relativePath + * underneath toPath. + */ + private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) { if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) { Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme"); - return false; + return null; } if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) { Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme"); - return false; + return null; } File fromFile = new File(fromPath.getSchemeSpecificPart()); - File toFile = new File(toPath.getSchemeSpecificPart(), relativePath); + File toFile = new File(toPath.getSchemeSpecificPart()); + if (toFile.isDirectory()) { + toFile = new File(toFile, relativePath); + } toFile.getParentFile().mkdirs(); - // TODO: This may not work if the two files are on different filesystems. Should we - // enforce that the temp file storage and the permanent storage are both in the same fs? - return fromFile.renameTo(toFile); + // TODO: This will not work if the two files are on different filesystems. Add manual + // copy later. + if (fromFile.renameTo(toFile)) { + return Uri.fromFile(toFile); + } + return null; } private static boolean verifyTempFilePath(Context context, DownloadRequest request, @@ -323,8 +356,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { * Returns a File linked to the directory used to store temp files for this request */ private static File getEmbmsTempFileDirForRequest(Context context, DownloadRequest request) { - File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir( - context, getFileProviderAuthority(context)); + File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context); // TODO: better naming scheme for temp file dirs String tempFileDirName = String.valueOf(request.getFileServiceInfo().getServiceId()); diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java index 6b905921dba0b..8260b728a52f4 100644 --- a/telephony/java/android/telephony/mbms/MbmsException.java +++ b/telephony/java/android/telephony/mbms/MbmsException.java @@ -22,7 +22,7 @@ public class MbmsException extends Exception { public static final int ERROR_NO_SERVICE_INSTALLED = 1; public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2; public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3; - public static final int ERROR_UNABLE_TO_INITIALIZE = 4; + public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 4; public static final int ERROR_ALREADY_INITIALIZED = 5; public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6; public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7; @@ -36,6 +36,7 @@ public class MbmsException extends Exception { public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15; public static final int ERROR_UNABLE_TO_READ_SIM = 16; public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17; + public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18; private final int mErrorCode; diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java index b3bc8146275eb..27d9878a1966b 100644 --- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java @@ -61,4 +61,17 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback. public void activeStreamingServicesUpdated(List services) { // default implementation empty } + + /** + * Called to indicate that the middleware has been initialized and is ready. + * + * Before this method is called, calling any method on an instance of + * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException} + * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} + * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY} + */ + @Override + public void middlewareReady() { + // default implementation empty + } } diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java index 9842581cdc021..c4d033bf2886f 100644 --- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java +++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java @@ -22,6 +22,7 @@ import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.database.Cursor; @@ -32,14 +33,15 @@ import android.os.ParcelFileDescriptor; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Objects; /** * @hide */ public class MbmsTempFileProvider extends ContentProvider { - public static final String META_DATA_USE_EXTERNAL_STORAGE = "use-external-storage"; - public static final String META_DATA_TEMP_FILE_DIRECTORY = "temp-file-path"; public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot"; + public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs"; + public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root"; private String mAuthority; private Context mContext; @@ -114,7 +116,7 @@ public class MbmsTempFileProvider extends ContentProvider { // Make sure the temp file is contained in the temp file directory as configured in the // manifest - File tempFileDir = getEmbmsTempFileDir(context, authority); + File tempFileDir = getEmbmsTempFileDir(context); if (!MbmsUtils.isContainedIn(tempFileDir, file)) { throw new IllegalArgumentException("File " + file + " is not contained in the temp " + "file directory, which is " + tempFileDir); @@ -147,13 +149,17 @@ public class MbmsTempFileProvider extends ContentProvider { if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { throw new IllegalArgumentException("Uri must have scheme content"); } + if (!Objects.equals(authority, uri.getAuthority())) { + throw new IllegalArgumentException("Uri does not have a matching authority: " + + authority + ", " + uri.getAuthority()); + } String relPath = Uri.decode(uri.getEncodedPath()); File file; File tempFileDir; try { - tempFileDir = getEmbmsTempFileDir(context, authority).getCanonicalFile(); + tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile(); file = new File(tempFileDir, relPath).getCanonicalFile(); } catch (IOException e) { throw new FileNotFoundException("Could not resolve paths"); @@ -169,25 +175,18 @@ public class MbmsTempFileProvider extends ContentProvider { /** * Returns a File for the directory used to store temp files for this app */ - public static File getEmbmsTempFileDir(Context context, String authority) { - Bundle metadata = getMetadata(context, authority); - File parentDirectory; - if (metadata.getBoolean(META_DATA_USE_EXTERNAL_STORAGE, false)) { - parentDirectory = context.getExternalFilesDir(null); - } else { - parentDirectory = context.getFilesDir(); + public static File getEmbmsTempFileDir(Context context) { + SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0); + String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null); + try { + if (storedTempFileRoot != null) { + return new File(storedTempFileRoot).getCanonicalFile(); + } else { + return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY) + .getCanonicalFile(); + } + } catch (IOException e) { + throw new RuntimeException("Unable to canonicalize temp file root path " + e); } - - String tmpFilePath = metadata.getString(META_DATA_TEMP_FILE_DIRECTORY); - if (tmpFilePath == null) { - tmpFilePath = DEFAULT_TOP_LEVEL_TEMP_DIRECTORY; - } - return new File(parentDirectory, tmpFilePath); - } - - private static Bundle getMetadata(Context context, String authority) { - final ProviderInfo info = context.getPackageManager() - .resolveContentProvider(authority, PackageManager.GET_META_DATA); - return info.metaData; } } diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java index de308053df56d..7d4727563eee4 100644 --- a/telephony/java/android/telephony/mbms/MbmsUtils.java +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -19,6 +19,7 @@ package android.telephony.mbms; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.pm.*; import android.content.pm.ServiceInfo; import android.telephony.MbmsDownloadManager; @@ -46,20 +47,6 @@ public class MbmsUtils { } } - public static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) { - long endTime = System.currentTimeMillis() + timeoutMs; - while (System.currentTimeMillis() < endTime) { - try { - l.await(timeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // keep waiting - } - if (l.getCount() <= 0) { - return; - } - } - } - public static ComponentName toComponentName(ComponentInfo ci) { return new ComponentName(ci.packageName, ci.name); } @@ -83,4 +70,19 @@ public class MbmsUtils { } return downloadServices.get(0).serviceInfo; } + + public static void startBinding(Context context, String serviceAction, + ServiceConnection serviceConnection) throws MbmsException { + Intent bindIntent = new Intent(); + ServiceInfo mbmsServiceInfo = + MbmsUtils.getMiddlewareServiceInfo(context, serviceAction); + + if (mbmsServiceInfo == null) { + throw new MbmsException(MbmsException.ERROR_NO_SERVICE_INSTALLED); + } + + bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo)); + + context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE); + } } diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index 6c2b8167d519f..ff7d233bbf2c4 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl @@ -47,6 +47,7 @@ interface IMbmsDownloadService */ int getFileServices(String appName, int subId, in List serviceClasses); + int setTempFileRootDirectory(String appName, int subId, String rootDirectoryPath); /** * should move the params into a DownloadRequest parcelable */ diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index 505aeae15355b..9577dd2e3d782 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -32,13 +32,19 @@ import java.util.List; */ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { @Override - public void initialize(String appName, int subId, IMbmsDownloadManagerCallback listener) - throws RemoteException { + public void initialize(String appName, int subscriptionId, + IMbmsDownloadManagerCallback listener) throws RemoteException { } @Override - public int getFileServices(String appName, int subId, List serviceClasses) throws - RemoteException { + public int getFileServices(String appName, int subscriptionId, List serviceClasses) + throws RemoteException { + return 0; + } + + @Override + public int setTempFileRootDirectory(String appName, int subscriptionId, + String rootDirectoryPath) throws RemoteException { return 0; }