Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d974296f authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Merge changes from topics 'embms-init-fix', 'embms-download-2'

am: e601160a

Change-Id: Icb84816dc98198ad6ed85dd8b030fbd0198d73a4
parents de1f0d76 e601160a
Loading
Loading
Loading
Loading
+222 −71
Original line number Diff line number Diff line
@@ -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() {
        MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
                new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                mService = IMbmsDownloadService.Stub.asInterface(service);
                latch.countDown();
                        IMbmsDownloadService downloadService =
                                IMbmsDownloadService.Stub.asInterface(service);
                        try {
                            downloadService.initialize(
                                    mDownloadAppName, mSubscriptionId, mCallback);
                        } catch (RemoteException e) {
                            Log.e(LOG_TAG, "Service died before initialization");
                            return;
                        }
                        mService.set(downloadService);
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                mService = null;
                        mService.set(null);
                    }
        };

        Intent bindIntent = new Intent();
        bindIntent.setComponent(MbmsUtils.toComponentName(
                MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION)));

        // Kick off the binding, and synchronously wait until binding is complete
        mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE);

        MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);

        // TODO: initialize
                });
    }

    /**
     * Gets the list of files published for download.
     * They may occur at times far in the future.
     * servicesClasses lets the app filter on types of files and is opaque data between
     *     the app and the carrier
     * An inspection API to retrieve the list of available
     * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
     * The results are returned asynchronously via a call to
     * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
     *
     * Multiple calls replace trhe list of serviceClasses of interest.
     * The serviceClasses argument lets the app filter on types of programming and is opaque data
     * negotiated beforehand between the app and the carrier.
     *
     * May throw an IllegalArgumentException or RemoteException.
     * Multiple calls replace the list of serviceClasses of interest.
     *
     * Synchronous responses include
     * <li>SUCCESS</li>
     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
     * This may throw an {@link MbmsException} containing one of the following errors:
     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
     * {@link MbmsException#ERROR_SERVICE_LOST}
     *
     * Asynchronous errors through the listener include any of the errors except
     * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li>
     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
     * <li>ERROR_MSDC_END_OF_SESSION</li>
     * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
     * callback can include any of the errors except:
     * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
     * {@link MbmsException#ERROR_END_OF_SESSION}
     */
    public int getFileServices(List<String> serviceClasses) {
        return 0;
    public void getFileServices(List<String> classList) throws MbmsException {
        IMbmsDownloadService downloadService = mService.get();
        if (downloadService == null) {
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
        }
        try {
            int returnCode = downloadService.getFileServices(
                    mDownloadAppName, mSubscriptionId, classList);
            if (returnCode != MbmsException.SUCCESS) {
                throw new MbmsException(returnCode);
            }
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Remote process died");
            mService.set(null);
            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
        }
    }

    /**
     * Sets the temp file root for downloads.
     * All temp files created for the middleware to write to will be contained in the specified
     * directory. Applications that wish to specify a location only need to call this method once
     * as long their data is persisted in storage -- the argument will be stored both in a
     * local instance of {@link android.content.SharedPreferences} and by the middleware.
     *
     * If this method is not called at least once before calling
     * {@link #download(DownloadRequest, IDownloadCallback)}, the framework
     * will default to a directory formed by the concatenation of the app's files directory and
     * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
     *
     * This method may not be called while any download requests are still active. If this is
     * the case, an {@link MbmsException} will be thrown with code
     * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
     *
     * The {@link File} supplied as a root temp file directory must already exist. If not, an
     * {@link IllegalArgumentException} will be thrown.
     * @param tempFileRootDirectory A directory to place temp files in.
     */
    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
            throws MbmsException {
        IMbmsDownloadService downloadService = mService.get();
        if (downloadService == null) {
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
        }
        if (!tempFileRootDirectory.exists()) {
            throw new IllegalArgumentException("Provided directory does not exist");
        }
        if (!tempFileRootDirectory.isDirectory()) {
            throw new IllegalArgumentException("Provided File is not a directory");
        }
        String filePath;
        try {
            filePath = tempFileRootDirectory.getCanonicalPath();
        } catch (IOException e) {
            throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
        }

        try {
            int result = downloadService.setTempFileRootDirectory(
                    mDownloadAppName, mSubscriptionId, filePath);
            if (result != MbmsException.SUCCESS) {
                throw new MbmsException(result);
            }
        } catch (RemoteException e) {
            mService.set(null);
            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
        }

        SharedPreferences prefs = mContext.getSharedPreferences(
                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
        prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
    }

    /**
     * Requests a future download.
     * returns a token which may be used to cancel a download.
     * Requests a download of a file that is available via multicast.
     *
     * downloadListener is an optional callback object which can be used to get progress reports
     *     of a currently occuring download.  Note this can only run while the calling app
     *     is running, so future downloads will simply result in resultIntents being sent
     *     for completed or errored-out downloads.  A NULL indicates no callbacks are needed.
     *
     * May throw an IllegalArgumentException or RemoteExcpetion.
     * May throw an {@link IllegalArgumentException}
     *
     * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
     * this method will create a directory at the default location defined at
     * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
     * file root directory.
     *
     * Asynchronous errors through the listener include any of the errors
     *
     * @param request The request that specifies what should be downloaded
     * @param callback Optional callback that will provide progress updates if the app is running.
     */
    public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) {
    public void download(DownloadRequest request, IDownloadCallback callback)
            throws MbmsException {
        IMbmsDownloadService downloadService = mService.get();
        if (downloadService == null) {
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
        }

        // Check to see whether the app's set a temp root dir yet, and set it if not.
        SharedPreferences prefs = mContext.getSharedPreferences(
                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
        if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
            File tempRootDirectory = new File(mContext.getFilesDir(),
                    MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
            tempRootDirectory.mkdirs();
            setTempFileRootDirectory(tempRootDirectory);
        }

        request.setAppName(mDownloadAppName);
        // Check if the request is a multipart download. If so, validate that the destination is
        // a directory that exists.
        // TODO: figure out what qualifies a request as a multipart download request.
        if (request.getSourceUri().getLastPathSegment() != null &&
                request.getSourceUri().getLastPathSegment().contains("*")) {
            File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
            if (!toFile.isDirectory()) {
                throw new IllegalArgumentException("Multipart download must specify valid " +
                        "destination directory.");
            }
        }
        // TODO: check to make sure destination is clear
        // TODO: write download request token
        try {
            mService.download(request, listener);
            downloadService.download(request, callback);
        } catch (RemoteException e) {
            mService = null;
            mService.set(null);
        }
        return request;
    }

    /**
@@ -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");
+39 −105

File changed.

Preview size limit exceeded, changes collapsed.

+12 −12
Original line number Diff line number Diff line
@@ -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() {
+27 −11
Original line number Diff line number Diff line
@@ -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;
    }
}
+7 −2
Original line number Diff line number Diff line
@@ -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;
    }

}
Loading