Merge changes from topics 'embms-init-fix', 'embms-download-2' am: e601160a96
am: d974296f8c
Change-Id: I61d4f04ec27b0298c9ab77dd92bf14d44a1ed93f
This commit is contained in:
@@ -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<IMbmsDownloadService> 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
|
||||
* <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) {
|
||||
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<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");
|
||||
|
||||
@@ -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<ServiceListener> mServiceListeners = new LinkedList<>();
|
||||
|
||||
private AtomicReference<IMbmsStreamingService> 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<String> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>() {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
@@ -68,4 +68,9 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable {
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<FileInfo> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<FileServiceInfo> services);
|
||||
|
||||
void middlewareReady();
|
||||
}
|
||||
|
||||
@@ -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<StreamingServiceInfo> 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<StreamingServiceInfo> services);
|
||||
|
||||
void middlewareReady();
|
||||
}
|
||||
|
||||
@@ -48,4 +48,17 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St
|
||||
public void fileServicesUpdated(List<FileServiceInfo> 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UriPathPair> generateUrisForPausedFiles(Context context,
|
||||
DownloadRequest request, List<Uri> 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<String> filePathComponents = info.uri.getPathSegments();
|
||||
List<String> filePathComponents = info.getUri().getPathSegments();
|
||||
List<String> requestPathComponents = request.getSourceUri().getPathSegments();
|
||||
Iterator<String> filePathIter = filePathComponents.iterator();
|
||||
Iterator<String> requestPathIter = requestPathComponents.iterator();
|
||||
|
||||
LinkedList<String> 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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -61,4 +61,17 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback.
|
||||
public void activeStreamingServicesUpdated(List<StreamingServiceInfo> 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ interface IMbmsDownloadService
|
||||
*/
|
||||
int getFileServices(String appName, int subId, in List<String> serviceClasses);
|
||||
|
||||
int setTempFileRootDirectory(String appName, int subId, String rootDirectoryPath);
|
||||
/**
|
||||
* should move the params into a DownloadRequest parcelable
|
||||
*/
|
||||
|
||||
@@ -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<String> serviceClasses) throws
|
||||
RemoteException {
|
||||
public int getFileServices(String appName, int subscriptionId, List<String> serviceClasses)
|
||||
throws RemoteException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setTempFileRootDirectory(String appName, int subscriptionId,
|
||||
String rootDirectoryPath) throws RemoteException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user