diff options
| author | 2018-09-25 15:02:39 -0700 | |
|---|---|---|
| committer | 2018-09-25 15:02:39 -0700 | |
| commit | abdaf5b51f798a4d9a1bae7b515df6ae40c2d591 (patch) | |
| tree | 4bd5da1f89860ad571d1b1a4b8f07e09087560e9 | |
| parent | 2bfca321cc832f44498a47313b54909903f08f94 (diff) | |
| parent | 210a4862f64c8374b2c04bfc2d12473ad52c5e66 (diff) | |
Merge "Add the EMBMS group call API" am: 52a8fdb7bd am: 6c6c859c6a
am: 210a4862f6
Change-Id: I3e7f4f0feb7b675a5870b9367fe788f58246d6fa
| -rw-r--r-- | Android.bp | 3 | ||||
| -rwxr-xr-x | api/current.txt | 38 | ||||
| -rw-r--r-- | api/system-current.txt | 15 | ||||
| -rw-r--r-- | api/test-current.txt | 15 | ||||
| -rw-r--r-- | telephony/java/android/telephony/MbmsGroupCallSession.java | 300 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/GroupCall.java | 176 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/GroupCallCallback.java | 87 | ||||
| -rwxr-xr-x | telephony/java/android/telephony/mbms/IGroupCallCallback.aidl | 26 | ||||
| -rwxr-xr-x | telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl | 33 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/InternalGroupCallCallback.java | 96 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java | 116 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java | 99 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/MbmsUtils.java | 4 | ||||
| -rwxr-xr-x | telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl | 39 | ||||
| -rw-r--r-- | telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java | 275 | 
15 files changed, 1322 insertions, 0 deletions
| diff --git a/Android.bp b/Android.bp index ba9156efe4af..3e471b63498e 100644 --- a/Android.bp +++ b/Android.bp @@ -515,11 +515,14 @@ java_defaults {          "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",          "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.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/IDownloadProgressListener.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/IMbmsStreamingService.aidl", +        "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",          "telephony/java/android/telephony/INetworkService.aidl",          "telephony/java/android/telephony/INetworkServiceCallback.aidl",          "telephony/java/com/android/ims/internal/IImsCallSession.aidl", diff --git a/api/current.txt b/api/current.txt index d897162b1aa3..bcd35d19ed1d 100755 --- a/api/current.txt +++ b/api/current.txt @@ -42531,6 +42531,13 @@ package android.telephony {      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 {      method public void close();      method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback); @@ -43480,6 +43487,29 @@ package android.telephony.mbms {      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 {      ctor public MbmsDownloadReceiver();      method public void onReceive(android.content.Context, android.content.Intent); @@ -43528,6 +43558,14 @@ package android.telephony.mbms {      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 {      ctor public MbmsStreamingSessionCallback();      method public void onError(int, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index 1009b672cf65..b8853e6d317b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5257,6 +5257,10 @@ package android.telephony {      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 {      field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";    } @@ -6558,6 +6562,17 @@ package android.telephony.mbms.vendor {      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 {      ctor public MbmsStreamingServiceBase();      method public void dispose(int) throws android.os.RemoteException; diff --git a/api/test-current.txt b/api/test-current.txt index f4d7cbcab6dc..956761615254 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1188,6 +1188,10 @@ package android.telephony {      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 {      field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";    } @@ -1257,6 +1261,17 @@ package android.telephony.mbms.vendor {      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 {      ctor public MbmsStreamingServiceBase();      method public void dispose(int) throws android.os.RemoteException; diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java new file mode 100644 index 000000000000..e3737976adf7 --- /dev/null +++ b/telephony/java/android/telephony/MbmsGroupCallSession.java @@ -0,0 +1,300 @@ +/* + * 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); +                    } +                }); +    } +} diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java new file mode 100644 index 000000000000..9aca18e07812 --- /dev/null +++ b/telephony/java/android/telephony/mbms/GroupCall.java @@ -0,0 +1,176 @@ +/* + * 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.mbms; + +import android.annotation.IntDef; +import android.os.RemoteException; +import android.telephony.MbmsGroupCallSession; +import android.telephony.mbms.vendor.IMbmsGroupCallService; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Class used to represent a single MBMS group call. After a call has been started with + * {@link MbmsGroupCallSession#startGroupCall}, + * this class is used to hold information about the call and control it. + */ +public class GroupCall implements AutoCloseable { +    private static final String LOG_TAG = "MbmsGroupCall"; + +    /** +     * The state of a group call, reported via +     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)} +     * @hide +     */ +    @Retention(RetentionPolicy.SOURCE) +    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED}) +    public @interface GroupCallState {} +    public static final int STATE_STOPPED = 1; +    public static final int STATE_STARTED = 2; +    public static final int STATE_STALLED = 3; + +    /** +     * The reason for a call state change, reported via +     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)} +     * @hide +     */ +    @Retention(RetentionPolicy.SOURCE) +    @IntDef(prefix = { "REASON_" }, +            value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT, +                    REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE, +                    REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE}) +    public @interface GroupCallStateChangeReason {} + +    /** +     * Indicates that the middleware does not have a reason to provide for the state change. +     */ +    public static final int REASON_NONE = 0; + +    /** +     * State changed due to a call to {@link #close()} or +     * {@link MbmsGroupCallSession#startGroupCall} +     */ +    public static final int REASON_BY_USER_REQUEST = 1; + +    // 2 is unused to match up with streaming. + +    /** +     * State changed due to a frequency conflict with another requested call. +     */ +    public static final int REASON_FREQUENCY_CONFLICT = 3; + +    /** +     * State changed due to the middleware running out of memory +     */ +    public static final int REASON_OUT_OF_MEMORY = 4; + +    /** +     * State changed due to the device leaving the home carrier's LTE network. +     */ +    public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; + +    /** +     * State changed due to the device leaving the area where this call is being broadcast. +     */ +    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; + +    private final int mSubscriptionId; +    private final long mTmgi; +    private final MbmsGroupCallSession mParentSession; +    private final InternalGroupCallCallback mCallback; +    private IMbmsGroupCallService mService; + +    /** +     * @hide +     */ +    public GroupCall(int subscriptionId, +            IMbmsGroupCallService service, +            MbmsGroupCallSession session, +            long tmgi, +            InternalGroupCallCallback callback) { +        mSubscriptionId = subscriptionId; +        mParentSession = session; +        mService = service; +        mTmgi = tmgi; +        mCallback = callback; +    } + +    /** +     * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call. +     */ +    public long getTmgi() { +        return mTmgi; +    } + +    /** +     * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency +     * information of the group call has * changed. Callers must obtain this information from the +     * wireless carrier independently. +     * @param saiArray New array of SAIs that the call is available on. +     * @param frequencyArray New array of frequencies that the call is available on. +     */ +    public void updateGroupCall(int[] saiArray, int[] frequencyArray) { +        if (mService == null) { +            throw new IllegalStateException("No group call service attached"); +        } + +        try { +            mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray); +        } catch (RemoteException e) { +            Log.w(LOG_TAG, "Remote process died"); +            mService = null; +            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); +        } finally { +            mParentSession.onGroupCallStopped(this); +        } +    } + +    /** +     * Stop this group call. Further operations on this object will fail with an +     * {@link IllegalStateException}. +     * +     * May throw an {@link IllegalStateException} +     */ +    @Override +    public void close() { +        if (mService == null) { +            throw new IllegalStateException("No group call service attached"); +        } + +        try { +            mService.stopGroupCall(mSubscriptionId, mTmgi); +        } catch (RemoteException e) { +            Log.w(LOG_TAG, "Remote process died"); +            mService = null; +            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); +        } finally { +            mParentSession.onGroupCallStopped(this); +        } +    } + +    /** @hide */ +    public InternalGroupCallCallback getCallback() { +        return mCallback; +    } + +    private void sendErrorToApp(int errorCode, String message) { +        mCallback.onError(errorCode, message); +    } +} + diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java new file mode 100644 index 000000000000..001bb02aad94 --- /dev/null +++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java @@ -0,0 +1,87 @@ +/* + * 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.mbms; + +import android.annotation.IntDef; +import android.annotation.Nullable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A callback class for use when the application is in a group call. The middleware + * will provide updates on the status of the call via this callback. + */ +public class GroupCallCallback { +    /** @hide */ +    @Retention(RetentionPolicy.SOURCE) +    @IntDef(value = { +            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE, +            MbmsErrors.ERROR_MIDDLEWARE_LOST, +            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND, +            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY, +            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY, +            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE, +            MbmsErrors.GeneralErrors.ERROR_IN_E911, +            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, +            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, +            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) +    private @interface GroupCallError{} + +    /** +     * Indicates broadcast signal strength is not available for this call. +     * +     * This may be due to the call no longer being available due to geography +     * or timing (end of service) +     */ +    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; + +    /** +     * Called by the middleware when it has detected an error condition in this group call. The +     * possible error codes are listed in {@link MbmsErrors}. +     * @param errorCode The error code. +     * @param message A human-readable message generated by the middleware for debugging purposes. +     */ +    public void onError(@GroupCallError int errorCode, @Nullable String message) { +        // default implementation empty +    } + +    /** +     * Called to indicate this call has changed state. +     * +     * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED} +     * and {@link GroupCall#STATE_STALLED}. +     */ +    public void onGroupCallStateChanged(@GroupCall.GroupCallState int state, +            @GroupCall.GroupCallStateChangeReason int reason) { +        // default implementation empty +    } + +    /** +     * Broadcast Signal Strength updated. +     * +     * This signal strength is the BROADCAST signal strength which, +     * depending on technology in play and it's deployment, may be +     * stronger or weaker than the traditional UNICAST signal +     * strength.  It a simple int from 0-4 for valid levels or +     * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available +     * for this call due to timing, geography or popularity. +     */ +    public void onBroadcastSignalStrengthUpdated(int signalStrength) { +        // default implementation empty +    } +} diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl new file mode 100755 index 000000000000..844b6344a34c --- /dev/null +++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl @@ -0,0 +1,26 @@ +/* + * 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.mbms; + +/** + * @hide + */ +oneway interface IGroupCallCallback { +    void onError(int errorCode, String message); +    void onGroupCallStateChanged(int state, int reason); +    void onBroadcastSignalStrengthUpdated(int signalStrength); +} diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl new file mode 100755 index 000000000000..1a1c7f8af5df --- /dev/null +++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl @@ -0,0 +1,33 @@ +/* +** Copyright 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.mbms; + +import java.util.List; + +/** + * @hide + */ +oneway interface IMbmsGroupCallSessionCallback +{ +    void onError(int errorCode, String message); + +    void onAvailableSaisUpdated(in List currentSai, in List availableSais); + +    void onServiceInterfaceAvailable(String interfaceName, int index); + +    void onMiddlewareReady(); +} diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java new file mode 100644 index 000000000000..2910bb313d84 --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java @@ -0,0 +1,96 @@ +/* + * 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.mbms; + +import android.os.Binder; + +import java.util.concurrent.Executor; + +/** @hide */ +public class InternalGroupCallCallback extends IGroupCallCallback.Stub { +    private final GroupCallCallback mAppCallback; +    private final Executor mExecutor; +    private volatile boolean mIsStopped = false; + +    public InternalGroupCallCallback(GroupCallCallback appCallback, +            Executor executor) { +        mAppCallback = appCallback; +        mExecutor = executor; +    } + +    @Override +    public void onError(final int errorCode, final String message) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onError(errorCode, message); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    @Override +    public void onGroupCallStateChanged(final int state, final int reason) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onGroupCallStateChanged(state, reason); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    @Override +    public void onBroadcastSignalStrengthUpdated(final int signalStrength) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    /** Prevents this callback from calling the app */ +    public void stop() { +        mIsStopped = true; +    } +} diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java new file mode 100644 index 000000000000..4c9cf4dd7c92 --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java @@ -0,0 +1,116 @@ +/* + * 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.mbms; + +import android.os.Binder; + +import java.util.List; +import java.util.concurrent.Executor; + +/** @hide */ +public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub { +    private final Executor mExecutor; +    private final MbmsGroupCallSessionCallback mAppCallback; +    private volatile boolean mIsStopped = false; + +    public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback, +            Executor executor) { +        mAppCallback = appCallback; +        mExecutor = executor; +    } + +    @Override +    public void onError(final int errorCode, final String message) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onError(errorCode, message); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    @Override +    public void onAvailableSaisUpdated(final List currentSais, final List availableSais) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onAvailableSaisUpdated(currentSais, availableSais); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    @Override +    public void onServiceInterfaceAvailable(final String interfaceName, final int index) { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onServiceInterfaceAvailable(interfaceName, index); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    @Override +    public void onMiddlewareReady() { +        if (mIsStopped) { +            return; +        } + +        mExecutor.execute(new Runnable() { +            @Override +            public void run() { +                long token = Binder.clearCallingIdentity(); +                try { +                    mAppCallback.onMiddlewareReady(); +                } finally { +                    Binder.restoreCallingIdentity(token); +                } +            } +        }); +    } + +    /** Prevents this callback from calling the app */ +    public void stop() { +        mIsStopped = true; +    } +} diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java new file mode 100644 index 000000000000..7da734ee5837 --- /dev/null +++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java @@ -0,0 +1,99 @@ +/* + * 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.mbms; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.content.Context; +import android.telephony.MbmsGroupCallSession; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * A callback class that is used to receive information from the middleware on MBMS group-call + * services. An instance of this object should be passed into + * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}. + */ +public class MbmsGroupCallSessionCallback { +    /** @hide */ +    @Retention(RetentionPolicy.SOURCE) +    @IntDef(value = { +            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE, +            MbmsErrors.ERROR_MIDDLEWARE_LOST, +            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND, +            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED, +            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, +            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, +            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY, +            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY, +            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE, +            MbmsErrors.GeneralErrors.ERROR_IN_E911, +            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, +            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, +            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) +    private @interface GroupCallError{} + +    /** +     * Called by the middleware when it has detected an error condition. The possible error codes +     * are listed in {@link MbmsErrors}. +     * @param errorCode The error code. +     * @param message A human-readable message generated by the middleware for debugging purposes. +     */ +    public void onError(@GroupCallError int errorCode, @Nullable String message) { +    } + +    /** +     * Indicates that the list of currently available SAIs has been updated. The app may use this +     * information to filter the list of group calls when displaying available group calls to +     * the user by matching the SAIs with a list of group calls separately negotiated with the +     * carrier. The app may also report the aggregate list of SAIs to the group call application +     * server so that a network entity can determine when, and where to activate the broadcast of +     * particular group calls. +     * @param currentSais The available SAIs on the current cell. +     * @param availableSais A list of lists of available SAIS in neighboring cells, where each list +     *                      contains the available SAIs in an individual cell. +     */ +    public void onAvailableSaisUpdated(List<Integer> currentSais, +            List<List<Integer>> availableSais) { +    } + +    /** +     * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied +     * via this callback may be used to establish a data-link interface with the modem before the +     * middleware is ready. +     * Note that this method may be called before {@link #onMiddlewareReady()}. +     * +     * @param interfaceName The interface name for the data link. +     * @param index The index for the data link. +     */ +    public void onServiceInterfaceAvailable(String interfaceName, int index) { +    } + +    /** +     * 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 MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error +     * delivered via {@link #onError(int, String)} with error code +     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}. +     */ +    public void onMiddlewareReady() { +    } +} diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java index 06b2120b59c1..95b4d37e5840 100644 --- a/telephony/java/android/telephony/mbms/MbmsUtils.java +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection;  import android.content.pm.*;  import android.content.pm.ServiceInfo;  import android.telephony.MbmsDownloadSession; +import android.telephony.MbmsGroupCallSession;  import android.telephony.MbmsStreamingSession;  import android.util.Log; @@ -59,6 +60,9 @@ public class MbmsUtils {              case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:                  metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;                  break; +            case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION: +                metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA; +                break;          }          if (metaDataKey == null) {              return null; diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl new file mode 100755 index 000000000000..721256a95396 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl @@ -0,0 +1,39 @@ +/* +** Copyright 2017, 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.mbms.vendor; + +import android.net.Uri; +import android.telephony.mbms.IMbmsGroupCallSessionCallback; +import android.telephony.mbms.IGroupCallCallback; + +/** + * @hide + */ +interface IMbmsGroupCallService +{ +    int initialize(IMbmsGroupCallSessionCallback callback, int subId); + +    void stopGroupCall(int subId, long tmgi); + +    void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray, +        in int[] frequencyArray); + +    int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray, +        in int[] frequencyArray, IGroupCallCallback callback); + +    void dispose(int subId); +} diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java new file mode 100644 index 000000000000..3734ca7d6fc9 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java @@ -0,0 +1,275 @@ +/* + * 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.mbms.vendor; + +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.mbms.GroupCallCallback; +import android.telephony.mbms.IGroupCallCallback; +import android.telephony.mbms.IMbmsGroupCallSessionCallback; +import android.telephony.mbms.MbmsErrors; +import android.telephony.mbms.MbmsGroupCallSessionCallback; +import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub; + +import java.util.List; + +/** + * Base class for MBMS group-call services. The middleware should override this class to implement + * its {@link Service} for group calls + * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they + * override {@link #onBind}. + * @hide + */ +@SystemApi +@TestApi +public class MbmsGroupCallServiceBase extends Service { +    private final IBinder mInterface = new Stub() { +        @Override +        public int initialize(final IMbmsGroupCallSessionCallback callback, +                final int subscriptionId) throws RemoteException { +            if (callback == null) { +                throw new NullPointerException("Callback must not be null"); +            } + +            final int uid = Binder.getCallingUid(); + +            int result = MbmsGroupCallServiceBase.this.initialize( +                    new MbmsGroupCallSessionCallback() { +                        @Override +                        public void onError(final int errorCode, final String message) { +                            try { +                                if (errorCode == MbmsErrors.UNKNOWN) { +                                    throw new IllegalArgumentException( +                                            "Middleware cannot send an unknown error."); +                                } +                                callback.onError(errorCode, message); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } + +                        @Override +                        public void onAvailableSaisUpdated(final List currentSais, +                                final List availableSais) { +                            try { +                                callback.onAvailableSaisUpdated(currentSais, availableSais); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } + +                        @Override +                        public void onServiceInterfaceAvailable(final String interfaceName, +                                final int index) { +                            try { +                                callback.onServiceInterfaceAvailable(interfaceName, index); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } + +                        @Override +                        public void onMiddlewareReady() { +                            try { +                                callback.onMiddlewareReady(); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } +                    }, subscriptionId); + +            if (result == MbmsErrors.SUCCESS) { +                callback.asBinder().linkToDeath(new DeathRecipient() { +                    @Override +                    public void binderDied() { +                        onAppCallbackDied(uid, subscriptionId); +                    } +                }, 0); +            } + +            return result; +        } + +        @Override +        public void stopGroupCall(int subId, long tmgi) { +            MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi); +        } + +        @Override +        public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, +                int[] frequencyArray) { +            MbmsGroupCallServiceBase.this.updateGroupCall( +                    subscriptionId, tmgi, saiArray, frequencyArray); +        } + +        @Override +        public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray, +                final int[] frequencyArray, final IGroupCallCallback callback) +                throws RemoteException { +            if (callback == null) { +                throw new NullPointerException("Callback must not be null"); +            } + +            final int uid = Binder.getCallingUid(); + +            int result = MbmsGroupCallServiceBase.this.startGroupCall( +                    subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() { +                        @Override +                        public void onError(final int errorCode, final String message) { +                            try { +                                if (errorCode == MbmsErrors.UNKNOWN) { +                                    throw new IllegalArgumentException( +                                            "Middleware cannot send an unknown error."); +                                } +                                callback.onError(errorCode, message); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } + +                        public void onGroupCallStateChanged(int state, int reason) { +                            try { +                                callback.onGroupCallStateChanged(state, reason); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } + +                        public void onBroadcastSignalStrengthUpdated(int signalStrength) { +                            try { +                                callback.onBroadcastSignalStrengthUpdated(signalStrength); +                            } catch (RemoteException e) { +                                onAppCallbackDied(uid, subscriptionId); +                            } +                        } +                    }); + +            if (result == MbmsErrors.SUCCESS) { +                callback.asBinder().linkToDeath(new DeathRecipient() { +                    @Override +                    public void binderDied() { +                        onAppCallbackDied(uid, subscriptionId); +                    } +                }, 0); +            } + +            return result; +        } + +        @Override +        public void dispose(int subId) throws RemoteException { +            MbmsGroupCallServiceBase.this.dispose(subId); +        } +    }; + +    /** +     * Initialize the group call service for this app and subscription ID, registering the callback. +     * +     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which +     * will be intercepted and passed to the app as +     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} +     * +     * May return any value from {@link MbmsErrors.InitializationErrors} +     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via +     * {@link IMbmsGroupCallSessionCallback#onError(int, String)}. +     * +     * @param callback The callback to use to communicate with the app. +     * @param subscriptionId The subscription ID to use. +     */ +    public int initialize(MbmsGroupCallSessionCallback callback, int subscriptionId) +            throws RemoteException { +        throw new UnsupportedOperationException("Not implemented"); +    } + +    /** +     * Starts a particular group call. This method may perform asynchronous work. When +     * the call is ready for consumption, the middleware should inform the app via +     * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}. +     * +     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} +     * +     * @param subscriptionId The subscription id to use. +     * @param tmgi The TMGI, an identifier for the group call. +     * @param saiArray An array of SAIs for the group call. +     * @param frequencyArray An array of frequencies for the group call. +     * @param callback The callback object on which the app wishes to receive updates. +     * @return Any error in {@link MbmsErrors.GeneralErrors} +     */ +    public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray, +            GroupCallCallback callback) { +        throw new UnsupportedOperationException("Not implemented"); +    } + +    /** +     * Stop the group call identified by {@code tmgi}. +     * +     * The callback provided via {@link #startGroupCall} should no longer be +     * used after this method has called by the app. +     * +     * May throw an {@link IllegalStateException} +     * +     * @param subscriptionId The subscription id to use. +     * @param tmgi The TMGI for the call to stop. +     */ +    public void stopGroupCall(int subscriptionId, long tmgi) { +        throw new UnsupportedOperationException("Not implemented"); +    } + +    /** +     * Called when the app receives new SAI and frequency information for the group call identified +     * by {@code tmgi}. +     * @param saiArray New array of SAIs that the call is available on. +     * @param frequencyArray New array of frequencies that the call is available on. +     */ +    public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, +            int[] frequencyArray) { +        throw new UnsupportedOperationException("Not implemented"); +    } + +    /** +     * Signals that the app wishes to dispose of the session identified by the +     * {@code subscriptionId} argument and the caller's uid. No notification back to the +     * app is required for this operation, and the corresponding callback provided via +     * {@link #initialize} should no longer be used +     * after this method has been called by the app. +     * +     * May throw an {@link IllegalStateException} +     * +     * @param subscriptionId The subscription id to use. +     */ +    public void dispose(int subscriptionId) throws RemoteException { +        throw new UnsupportedOperationException("Not implemented"); +    } + +    /** +     * Indicates that the app identified by the given UID and subscription ID has died. +     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}. +     * @param subscriptionId The subscription ID the app is using. +     */ +    public void onAppCallbackDied(int uid, int subscriptionId) { +    } + +    @Override +    public IBinder onBind(Intent intent) { +        return mInterface; +    } +} |