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

Commit b9d2b56c authored by Hall Liu's avatar Hall Liu Committed by Android (Google) Code Review
Browse files

Merge changes from topic "embms-cherry-pick-3" into oc-mr1-dev

* changes:
  API council suggested edits, part 3
  Apply suggested edits to the file-download API
  Make changes to MBMS Streaming APIs per recommendations
parents e0298368 bfc5f1c9
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -502,8 +502,8 @@ LOCAL_SRC_FILES += \
	telecomm/java/com/android/internal/telecom/IInCallService.aidl \
	telecomm/java/com/android/internal/telecom/ITelecomService.aidl \
	telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl \
        telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl \
	telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl \
	telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl \
	telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl \
	telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \
        telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \
	telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl \
+104 −97
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.telephony;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.ComponentName;
@@ -25,18 +27,20 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.telephony.mbms.InternalStreamingManagerCallback;
import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback;
import android.telephony.mbms.MbmsException;
import android.telephony.mbms.MbmsStreamingManagerCallback;
import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.ArraySet;
import android.util.Log;

import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@@ -46,8 +50,8 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 * This class provides functionality for streaming media over MBMS.
 * @hide
 */
public class MbmsStreamingManager {
    private static final String LOG_TAG = "MbmsStreamingManager";
public class MbmsStreamingSession implements AutoCloseable {
    private static final String LOG_TAG = "MbmsStreamingSession";

    /**
     * Service action which must be handled by the middleware implementing the MBMS streaming
@@ -66,98 +70,98 @@ public class MbmsStreamingManager {
        @Override
        public void binderDied() {
            sIsInitialized.set(false);
            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
        }
    };

    private InternalStreamingManagerCallback mInternalCallback;
    private InternalStreamingSessionCallback mInternalCallback;
    private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();

    private final Context mContext;
    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;

    /** @hide */
    private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
                    int subscriptionId, Handler handler) {
        mContext = context;
        mSubscriptionId = subscriptionId;
        if (handler == null) {
            handler = new Handler(Looper.getMainLooper());
        }
        mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
    }

    /**
     * Create a new MbmsStreamingManager using the given subscription ID.
     * Create a new {@link MbmsStreamingSession} using the given subscription ID.
     *
     * Note that this call will bind a remote service. You may not call this method on your app's
     * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
     * during the initialization or binding process.
     * main thread.
     *
     *
     * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
     * method while there is an active instance of {@link MbmsStreamingManager} in your process
     * (in other words, one that has not had {@link #dispose()} called on it), this method will
     * throw an {@link MbmsException}. If you call this method in a different process
     * You may only have one instance of {@link MbmsStreamingSession} per UID. If you call this
     * method while there is an active instance of {@link MbmsStreamingSession} in your process
     * (in other words, one that has not had {@link #close()} called on it), this method will
     * throw an {@link IllegalStateException}. If you call this method in a different process
     * running under the same UID, an error will be indicated via
     * {@link MbmsStreamingManagerCallback#onError(int, String)}.
     * {@link MbmsStreamingSessionCallback#onError(int, String)}.
     *
     * Note that initialization may fail asynchronously. If you wish to try again after you
     * receive such an asynchronous error, you must call dispose() on the instance of
     * {@link MbmsStreamingManager} that you received before calling this method again.
     * receive such an asynchronous error, you must call {@link #close()} on the instance of
     * {@link MbmsStreamingSession} that you received before calling this method again.
     *
     * @param context The {@link Context} to use.
     * @param callback A callback object on which you wish to receive results of asynchronous
     *                 operations.
     * @param subscriptionId The subscription ID to use.
     * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
     *                processed on the main looper (in other words, the looper returned from
     *                {@link Looper#getMainLooper()}).
     * @param handler The handler you wish to receive callbacks on.
     * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
     */
    public static MbmsStreamingManager create(Context context,
            MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
            throws MbmsException {
    public static @Nullable MbmsStreamingSession create(@NonNull Context context,
            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
            @NonNull Handler handler) {
        if (!sIsInitialized.compareAndSet(false, true)) {
            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
            throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
        }
        MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
        MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
                subscriptionId, handler);
        try {
            manager.bindAndInitialize();
        } catch (MbmsException e) {

        final int result = session.bindAndInitialize();
        if (result != MbmsErrors.SUCCESS) {
            sIsInitialized.set(false);
            throw e;
            handler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onError(result, null);
                }
        return manager;
            });
            return null;
        }

    /**
     * Create a new MbmsStreamingManager using the system default data subscription ID.
     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
     */
    public static MbmsStreamingManager create(Context context,
            MbmsStreamingManagerCallback callback, Handler handler)
            throws MbmsException {
        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
        return session;
    }

    /**
     * Create a new MbmsStreamingManager using the system default data subscription ID and
     * default {@link Handler}.
     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
     * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
     */
    public static MbmsStreamingManager create(Context context,
            MbmsStreamingManagerCallback callback)
            throws MbmsException {
        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
    public static MbmsStreamingSession create(@NonNull Context context,
            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
    }

    /**
     * Terminates this instance, ending calls to the registered listener.  Also terminates
     * any streaming services spawned from this instance.
     * Terminates this instance. Also terminates
     * any streaming services spawned from this instance as if
     * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
     * no further callbacks originating from the middleware will be enqueued on the provided
     * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
     * enqueued will still be delivered.
     *
     * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
     * obtain another instance of {@link MbmsStreamingSession} immediately after this method
     * returns.
     *
     * May throw an {@link IllegalStateException}
     */
    public void dispose() {
    public void close() {
        try {
            IMbmsStreamingService streamingService = mService.get();
            if (streamingService == null) {
@@ -165,47 +169,49 @@ public class MbmsStreamingManager {
                return;
            }
            streamingService.dispose(mSubscriptionId);
            for (StreamingService s : mKnownActiveStreamingServices) {
                s.getCallback().stop();
            }
            mKnownActiveStreamingServices.clear();
        } catch (RemoteException e) {
            // Ignore for now
        } finally {
            mService.set(null);
            sIsInitialized.set(false);
            mInternalCallback.stop();
        }
    }

    /**
     * An inspection API to retrieve the list of streaming media currently be advertised.
     * The results are returned asynchronously through the previously registered callback.
     * serviceClasses lets the app filter on types of programming and is opaque data between
     * the app and the carrier.
     *
     * Multiple calls replace the list of serviceClasses of interest.
     * The results are returned asynchronously via
     * {@link MbmsStreamingSessionCallback#onStreamingServicesUpdated(List)} on the callback
     * provided upon creation.
     *
     * This may throw an {@link MbmsException} containing any error in
     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
     * Multiple calls replace the list of service classes of interest.
     *
     * May also throw an unchecked {@link IllegalArgumentException} or an
     * {@link IllegalStateException}
     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
     *
     * @param classList A list of streaming service classes that the app would like updates on.
     * @param serviceClassList A list of streaming service classes that the app would like updates
     *                         on. The exact names of these classes should be negotiated with the
     *                         wireless carrier separately.
     */
    public void getStreamingServices(List<String> classList) throws MbmsException {
    public void requestUpdateStreamingServices(List<String> serviceClassList) {
        IMbmsStreamingService streamingService = mService.get();
        if (streamingService == null) {
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
            throw new IllegalStateException("Middleware not yet bound");
        }
        try {
            int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList);
            if (returnCode != MbmsException.SUCCESS) {
                throw new MbmsException(returnCode);
            int returnCode = streamingService.requestUpdateStreamingServices(
                    mSubscriptionId, serviceClassList);
            if (returnCode != MbmsErrors.SUCCESS) {
                sendErrorToApp(returnCode, null);
            }
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Remote process died");
            mService.set(null);
            sIsInitialized.set(false);
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
        }
    }

@@ -216,56 +222,57 @@ public class MbmsStreamingManager {
     * reported via
     * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
     *
     * May throw an
     * {@link MbmsException} containing any of the error codes in
     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
     *
     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
     *
     * Asynchronous errors through the callback include any of the errors in
     * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
     * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
     * {@link MbmsErrors.GeneralErrors} or
     * {@link MbmsErrors.StreamingErrors}.
     *
     * @param serviceInfo The information about the service to stream.
     * @param callback A callback that'll be called when something about the stream changes.
     * @param handler A handler that calls to {@code callback} should be called on. If null,
     *                defaults to the handler provided via
     *                {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
     * @param handler A handler that calls to {@code callback} should be called on.
     * @return An instance of {@link StreamingService} through which the stream can be controlled.
     *         May be {@code null} if an error occurred.
     */
    public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
            StreamingServiceCallback callback, Handler handler) throws MbmsException {
    public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
            StreamingServiceCallback callback, @NonNull Handler handler) {
        IMbmsStreamingService streamingService = mService.get();
        if (streamingService == null) {
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
            throw new IllegalStateException("Middleware not yet bound");
        }

        InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
                callback, handler == null ? mInternalCallback.getHandler() : handler);
                callback, handler);

        StreamingService serviceForApp = new StreamingService(
                mSubscriptionId, streamingService, serviceInfo, serviceCallback);
                mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
        mKnownActiveStreamingServices.add(serviceForApp);

        try {
            int returnCode = streamingService.startStreaming(
                    mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
            if (returnCode != MbmsException.SUCCESS) {
                throw new MbmsException(returnCode);
            if (returnCode != MbmsErrors.SUCCESS) {
                sendErrorToApp(returnCode, null);
                return null;
            }
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Remote process died");
            mService.set(null);
            sIsInitialized.set(false);
            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
            return null;
        }

        return serviceForApp;
    }

    private void bindAndInitialize() throws MbmsException {
        MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
    /** @hide */
    public void onStreamingServiceStopped(StreamingService service) {
        mKnownActiveStreamingServices.remove(service);
    }

    private int bindAndInitialize() {
        return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
                new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
@@ -278,19 +285,19 @@ public class MbmsStreamingManager {
                        } catch (RemoteException e) {
                            Log.e(LOG_TAG, "Service died before initialization");
                            sendErrorToApp(
                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    e.toString());
                            sIsInitialized.set(false);
                            return;
                        } catch (RuntimeException e) {
                            Log.e(LOG_TAG, "Runtime exception during initialization");
                            sendErrorToApp(
                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    e.toString());
                            sIsInitialized.set(false);
                            return;
                        }
                        if (result != MbmsException.SUCCESS) {
                        if (result != MbmsErrors.SUCCESS) {
                            sendErrorToApp(result, "Error returned during initialization");
                            sIsInitialized.set(false);
                            return;
@@ -298,7 +305,7 @@ public class MbmsStreamingManager {
                        try {
                            streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
                        } catch (RemoteException e) {
                            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
                            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
                                    "Middleware lost during initialization");
                            sIsInitialized.set(false);
                            return;
@@ -316,7 +323,7 @@ public class MbmsStreamingManager {

    private void sendErrorToApp(int errorCode, String message) {
        try {
            mInternalCallback.error(errorCode, message);
            mInternalCallback.onError(errorCode, message);
        } catch (RemoteException e) {
            // Ignore, should not happen locally.
        }
+6 −41
Original line number Diff line number Diff line
@@ -56,12 +56,10 @@ public final class DownloadRequest implements Parcelable {

    /** @hide */
    private static class OpaqueDataContainer implements Serializable {
        private final String destinationUri;
        private final String appIntent;
        private final int version;

        public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
            this.destinationUri = destinationUri;
        public OpaqueDataContainer(String appIntent, int version) {
            this.appIntent = appIntent;
            this.version = version;
        }
@@ -70,7 +68,6 @@ public final class DownloadRequest implements Parcelable {
    public static class Builder {
        private String fileServiceId;
        private Uri source;
        private Uri dest;
        private int subscriptionId;
        private String appIntent;
        private int version = CURRENT_VERSION;
@@ -104,21 +101,6 @@ public final class DownloadRequest implements Parcelable {
            return this;
        }

        /**
         * Sets the destination URI for the download request to be built. The middleware should
         * not set this directly.
         * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
         *             final destination of the download.
         */
        public Builder setDest(Uri dest) {
            if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
                throw new IllegalArgumentException("Destination uri must not exceed length " +
                        MAX_DESTINATION_URI_SIZE);
            }
            this.dest = dest;
            return this;
        }

        /**
         * Set the subscription ID on which the file(s) should be downloaded.
         * @param subscriptionId
@@ -160,7 +142,6 @@ public final class DownloadRequest implements Parcelable {
                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
                version = dataContainer.version;
                appIntent = dataContainer.appIntent;
                dest = Uri.parse(dataContainer.destinationUri);
            } catch (IOException e) {
                // Really should never happen
                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
@@ -173,24 +154,21 @@ public final class DownloadRequest implements Parcelable {
        }

        public DownloadRequest build() {
            return new DownloadRequest(fileServiceId, source, dest,
                    subscriptionId, appIntent, version);
            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
        }
    }

    private final String fileServiceId;
    private final Uri sourceUri;
    private final Uri destinationUri;
    private final int subscriptionId;
    private final String serializedResultIntentForApp;
    private final int version;

    private DownloadRequest(String fileServiceId,
            Uri source, Uri dest,
            int sub, String appIntent, int version) {
            Uri source, int sub,
            String appIntent, int version) {
        this.fileServiceId = fileServiceId;
        sourceUri = source;
        destinationUri = dest;
        subscriptionId = sub;
        serializedResultIntentForApp = appIntent;
        this.version = version;
@@ -203,7 +181,6 @@ public final class DownloadRequest implements Parcelable {
    private DownloadRequest(DownloadRequest dr) {
        fileServiceId = dr.fileServiceId;
        sourceUri = dr.sourceUri;
        destinationUri = dr.destinationUri;
        subscriptionId = dr.subscriptionId;
        serializedResultIntentForApp = dr.serializedResultIntentForApp;
        version = dr.version;
@@ -212,7 +189,6 @@ public final class DownloadRequest implements Parcelable {
    private DownloadRequest(Parcel in) {
        fileServiceId = in.readString();
        sourceUri = in.readParcelable(getClass().getClassLoader());
        destinationUri = in.readParcelable(getClass().getClassLoader());
        subscriptionId = in.readInt();
        serializedResultIntentForApp = in.readString();
        version = in.readInt();
@@ -225,7 +201,6 @@ public final class DownloadRequest implements Parcelable {
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(fileServiceId);
        out.writeParcelable(sourceUri, flags);
        out.writeParcelable(destinationUri, flags);
        out.writeInt(subscriptionId);
        out.writeString(serializedResultIntentForApp);
        out.writeInt(version);
@@ -245,14 +220,6 @@ public final class DownloadRequest implements Parcelable {
        return sourceUri;
    }

    /**
     * For use by the client app only.
     * @return The URI of the final destination of the download.
     */
    public Uri getDestinationUri() {
        return destinationUri;
    }

    /**
     * @return The subscription ID on which to perform MBMS operations.
     */
@@ -286,7 +253,7 @@ public final class DownloadRequest implements Parcelable {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
            OpaqueDataContainer container = new OpaqueDataContainer(
                    destinationUri.toString(), serializedResultIntentForApp, version);
                    serializedResultIntentForApp, version);
            stream.writeObject(container);
            stream.flush();
            return byteArrayOutputStream.toByteArray();
@@ -352,7 +319,6 @@ public final class DownloadRequest implements Parcelable {
        if (version >= 1) {
            // Hash the source URI, destination URI, and the app intent
            digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
            digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
        }
        // Add updates for future versions here
@@ -373,13 +339,12 @@ public final class DownloadRequest implements Parcelable {
                version == request.version &&
                Objects.equals(fileServiceId, request.fileServiceId) &&
                Objects.equals(sourceUri, request.sourceUri) &&
                Objects.equals(destinationUri, request.destinationUri) &&
                Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
    }

    @Override
    public int hashCode() {
        return Objects.hash(fileServiceId, sourceUri, destinationUri,
        return Objects.hash(fileServiceId, sourceUri,
                subscriptionId, serializedResultIntentForApp, version);
    }
}
+4 −6
Original line number Diff line number Diff line
@@ -16,14 +16,12 @@

package android.telephony.mbms;

import android.os.Handler;
import android.telephony.MbmsDownloadManager;
import android.telephony.MbmsDownloadSession;

/**
 * A optional listener class used by download clients to track progress. Apps should extend this
 * class and pass an instance into
 * {@link android.telephony.MbmsDownloadManager#download(
 * DownloadRequest, DownloadStateCallback, Handler)}
 * {@link MbmsDownloadSession#download(DownloadRequest)}
 *
 * This is optionally specified when requesting a download and will only be called while the app
 * is running.
@@ -59,7 +57,7 @@ public class DownloadStateCallback {
     *   may not have been able to get a list of them in advance.
     * @param state The current state of the download.
     */
    public void onStateChanged(DownloadRequest request, FileInfo fileInfo,
            @MbmsDownloadManager.DownloadStatus int state) {
    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
            @MbmsDownloadSession.DownloadStatus int state) {
    }
}
Loading