diff options
| -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; + } +} |