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

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

Merge "Add the EMBMS group call API" am: 52a8fdb7

am: 6c6c859c

Change-Id: I7e98519115c67eaa49694a3514b80fc54afcc17b
parents 3d54533e 6c6c859c
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -506,11 +506,14 @@ java_defaults {
        "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
        "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
        "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
        "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
        "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
        "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
        "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
        "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
        "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
        "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
        "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
        "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
        "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
        "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
        "telephony/java/android/telephony/INetworkService.aidl",
        "telephony/java/android/telephony/INetworkService.aidl",
        "telephony/java/android/telephony/INetworkServiceCallback.aidl",
        "telephony/java/android/telephony/INetworkServiceCallback.aidl",
        "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
        "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
+38 −0
Original line number Original line Diff line number Diff line
@@ -42250,6 +42250,13 @@ package android.telephony {
    field public static final int STATUS_UNKNOWN = 0; // 0x0
    field public static final int STATUS_UNKNOWN = 0; // 0x0
  }
  }
  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
    method public void close();
    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
    method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
  }
  public class MbmsStreamingSession implements java.lang.AutoCloseable {
  public class MbmsStreamingSession implements java.lang.AutoCloseable {
    method public void close();
    method public void close();
    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
@@ -43195,6 +43202,29 @@ package android.telephony.mbms {
    field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
    field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
  }
  }
  public class GroupCall implements java.lang.AutoCloseable {
    method public void close();
    method public long getTmgi();
    method public void updateGroupCall(int[], int[]);
    field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
    field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
    field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
    field public static final int REASON_NONE = 0; // 0x0
    field public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; // 0x5
    field public static final int REASON_OUT_OF_MEMORY = 4; // 0x4
    field public static final int STATE_STALLED = 3; // 0x3
    field public static final int STATE_STARTED = 2; // 0x2
    field public static final int STATE_STOPPED = 1; // 0x1
  }
  public class GroupCallCallback {
    ctor public GroupCallCallback();
    method public void onBroadcastSignalStrengthUpdated(int);
    method public void onError(int, java.lang.String);
    method public void onGroupCallStateChanged(int, int);
    field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
  }
  public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
  public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
    ctor public MbmsDownloadReceiver();
    ctor public MbmsDownloadReceiver();
    method public void onReceive(android.content.Context, android.content.Intent);
    method public void onReceive(android.content.Context, android.content.Intent);
@@ -43243,6 +43273,14 @@ package android.telephony.mbms {
    field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
    field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
  }
  }
  public class MbmsGroupCallSessionCallback {
    ctor public MbmsGroupCallSessionCallback();
    method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
    method public void onError(int, java.lang.String);
    method public void onMiddlewareReady();
    method public void onServiceInterfaceAvailable(java.lang.String, int);
  }
  public class MbmsStreamingSessionCallback {
  public class MbmsStreamingSessionCallback {
    ctor public MbmsStreamingSessionCallback();
    ctor public MbmsStreamingSessionCallback();
    method public void onError(int, java.lang.String);
    method public void onError(int, java.lang.String);
+15 −0
Original line number Original line Diff line number Diff line
@@ -5075,6 +5075,10 @@ package android.telephony {
    field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
    field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
  }
  }


  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
    field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
  }

  public class MbmsStreamingSession implements java.lang.AutoCloseable {
  public class MbmsStreamingSession implements java.lang.AutoCloseable {
    field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
    field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
  }
  }
@@ -6375,6 +6379,17 @@ package android.telephony.mbms.vendor {
    method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
    method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
  }
  }


  public class MbmsGroupCallServiceBase extends android.app.Service {
    ctor public MbmsGroupCallServiceBase();
    method public void dispose(int) throws android.os.RemoteException;
    method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
    method public void onAppCallbackDied(int, int);
    method public android.os.IBinder onBind(android.content.Intent);
    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
    method public void stopGroupCall(int, long);
    method public void updateGroupCall(int, long, int[], int[]);
  }

  public class MbmsStreamingServiceBase extends android.os.Binder {
  public class MbmsStreamingServiceBase extends android.os.Binder {
    ctor public MbmsStreamingServiceBase();
    ctor public MbmsStreamingServiceBase();
    method public void dispose(int) throws android.os.RemoteException;
    method public void dispose(int) throws android.os.RemoteException;
+15 −0
Original line number Original line Diff line number Diff line
@@ -966,6 +966,10 @@ package android.telephony {
    field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
    field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
  }
  }


  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
    field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = "mbms-group-call-service-override";
  }

  public class MbmsStreamingSession implements java.lang.AutoCloseable {
  public class MbmsStreamingSession implements java.lang.AutoCloseable {
    field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
    field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
  }
  }
@@ -1035,6 +1039,17 @@ package android.telephony.mbms.vendor {
    method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
    method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
  }
  }


  public class MbmsGroupCallServiceBase extends android.app.Service {
    ctor public MbmsGroupCallServiceBase();
    method public void dispose(int) throws android.os.RemoteException;
    method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
    method public void onAppCallbackDied(int, int);
    method public android.os.IBinder onBind(android.content.Intent);
    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
    method public void stopGroupCall(int, long);
    method public void updateGroupCall(int, long, int[], int[]);
  }

  public class MbmsStreamingServiceBase extends android.os.Binder {
  public class MbmsStreamingServiceBase extends android.os.Binder {
    ctor public MbmsStreamingServiceBase();
    ctor public MbmsStreamingServiceBase();
    method public void dispose(int) throws android.os.RemoteException;
    method public void dispose(int) throws android.os.RemoteException;
+300 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.telephony;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.mbms.GroupCall;
import android.telephony.mbms.GroupCallCallback;
import android.telephony.mbms.InternalGroupCallCallback;
import android.telephony.mbms.InternalGroupCallSessionCallback;
import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsGroupCallSessionCallback;
import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.vendor.IMbmsGroupCallService;
import android.util.ArraySet;
import android.util.Log;

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

/**
 * This class provides functionality for accessing group call functionality over MBMS.
 */
public class MbmsGroupCallSession implements AutoCloseable {
    private static final String LOG_TAG = "MbmsGroupCallSession";

    /**
     * Service action which must be handled by the middleware implementing the MBMS group call
     * interface.
     * @hide
     */
    @SystemApi
    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
    public static final String MBMS_GROUP_CALL_SERVICE_ACTION =
            "android.telephony.action.EmbmsGroupCall";

    /**
     * Metadata key that specifies the component name of the service to bind to for group calls.
     * @hide
     */
    @TestApi
    public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA =
            "mbms-group-call-service-override";

    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);

    private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null);
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            sIsInitialized.set(false);
            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
                    "Received death notification");
        }
    };

    private InternalGroupCallSessionCallback mInternalCallback;
    private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();

    private final Context mContext;
    private int mSubscriptionId;

    /** @hide */
    private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId,
            MbmsGroupCallSessionCallback callback) {
        mContext = context;
        mSubscriptionId = subscriptionId;
        mInternalCallback = new InternalGroupCallSessionCallback(callback, executor);
    }

    /**
     * Create a new {@link MbmsGroupCallSession} using the given subscription ID.
     *
     * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this
     * method while there is an active instance of {@link MbmsGroupCallSession} 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 MbmsGroupCallSessionCallback#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 {@link #close()} on the instance of
     * {@link MbmsGroupCallSession} that you received before calling this method again.
     *
     * @param context The {@link Context} to use.
     * @param executor The executor on which you wish to execute callbacks.
     * @param subscriptionId The subscription ID to use.
     * @param callback A callback object on which you wish to receive results of asynchronous
     *                 operations.
     * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
     */
    public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
            @NonNull Executor executor, int subscriptionId,
            final @NonNull MbmsGroupCallSessionCallback callback) {
        if (!sIsInitialized.compareAndSet(false, true)) {
            throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
        }
        MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor,
                subscriptionId, callback);

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

    /**
     * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
     * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
     */
    public static MbmsGroupCallSession create(@NonNull Context context,
            @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
    }

    /**
     * Terminates this instance. Also terminates
     * any group calls spawned from this instance as if
     * {@link GroupCall#close()} 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 MbmsGroupCallSessionCallback}, but callbacks that have already been
     * enqueued will still be delivered.
     *
     * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
     * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
     * returns.
     *
     * May throw an {@link IllegalStateException}
     */
    public void close() {
        try {
            IMbmsGroupCallService groupCallService = mService.get();
            if (groupCallService == null) {
                // Ignore and return, assume already disposed.
                return;
            }
            groupCallService.dispose(mSubscriptionId);
            for (GroupCall s : mKnownActiveGroupCalls) {
                s.getCallback().stop();
            }
            mKnownActiveGroupCalls.clear();
        } catch (RemoteException e) {
            // Ignore for now
        } finally {
            mService.set(null);
            sIsInitialized.set(false);
            mInternalCallback.stop();
        }
    }

    /**
     * Starts the requested group call, reporting status to the indicated callback.
     * Returns an object used to control that call.
     *
     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
     *
     * Asynchronous errors through the callback include any of the errors in
     * {@link MbmsErrors.GeneralErrors}.
     *
     * @param executor The executor on which you wish to execute callbacks for this stream.
     * @param tmgi The TMGI, an identifier for the group call you want to join.
     * @param saiArray An array of SAIs for the group call that should be negotiated separately with
     *                the carrier.
     * @param frequencyArray An array of frequencies for the group call that should be negotiated
     *                separately with the carrier.
     * @param callback The callback that you want to receive information about the call on.
     * @return An instance of {@link GroupCall} through which the call can be controlled.
     *         May be {@code null} if an error occurred.
     */
    public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
            int[] frequencyArray, @NonNull GroupCallCallback callback) {
        IMbmsGroupCallService groupCallService = mService.get();
        if (groupCallService == null) {
            throw new IllegalStateException("Middleware not yet bound");
        }

        InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback(
                callback, executor);

        GroupCall serviceForApp = new GroupCall(mSubscriptionId,
                groupCallService, this, tmgi, serviceCallback);
        mKnownActiveGroupCalls.add(serviceForApp);

        try {
            int returnCode = groupCallService.startGroupCall(
                    mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
            if (returnCode == MbmsErrors.UNKNOWN) {
                // Unbind and throw an obvious error
                close();
                throw new IllegalStateException("Middleware must not return an unknown error code");
            }
            if (returnCode != MbmsErrors.SUCCESS) {
                mInternalCallback.onError(returnCode, null);
                return null;
            }
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Remote process died");
            mService.set(null);
            sIsInitialized.set(false);
            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
            return null;
        }

        return serviceForApp;
    }

    /** @hide */
    public void onGroupCallStopped(GroupCall service) {
        mKnownActiveGroupCalls.remove(service);
    }

    private int bindAndInitialize() {
        return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
                new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        IMbmsGroupCallService groupCallService =
                                IMbmsGroupCallService.Stub.asInterface(service);
                        int result;
                        try {
                            result = groupCallService.initialize(mInternalCallback,
                                    mSubscriptionId);
                        } catch (RemoteException e) {
                            Log.e(LOG_TAG, "Service died before initialization");
                            mInternalCallback.onError(
                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    e.toString());
                            sIsInitialized.set(false);
                            return;
                        } catch (RuntimeException e) {
                            Log.e(LOG_TAG, "Runtime exception during initialization");
                            mInternalCallback.onError(
                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
                                    e.toString());
                            sIsInitialized.set(false);
                            return;
                        }
                        if (result == MbmsErrors.UNKNOWN) {
                            // Unbind and throw an obvious error
                            close();
                            throw new IllegalStateException("Middleware must not return"
                                    + " an unknown error code");
                        }
                        if (result != MbmsErrors.SUCCESS) {
                            mInternalCallback.onError(result,
                                    "Error returned during initialization");
                            sIsInitialized.set(false);
                            return;
                        }
                        try {
                            groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
                        } catch (RemoteException e) {
                            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
                                    "Middleware lost during initialization");
                            sIsInitialized.set(false);
                            return;
                        }
                        mService.set(groupCallService);
                    }

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