1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.telephony.mbms.vendor; 18 19 import android.annotation.NonNull; 20 import android.annotation.SystemApi; 21 import android.app.Service; 22 import android.content.Intent; 23 import android.os.Binder; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.telephony.mbms.GroupCallCallback; 27 import android.telephony.mbms.IGroupCallCallback; 28 import android.telephony.mbms.IMbmsGroupCallSessionCallback; 29 import android.telephony.mbms.MbmsErrors; 30 import android.telephony.mbms.MbmsGroupCallSessionCallback; 31 import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub; 32 33 import java.util.List; 34 35 /** 36 * Base class for MBMS group-call services. The middleware should override this class to implement 37 * its {@link Service} for group calls 38 * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they 39 * override {@link #onBind}. 40 * @hide 41 */ 42 @SystemApi 43 public class MbmsGroupCallServiceBase extends Service { 44 private final IBinder mInterface = new Stub() { 45 @Override 46 public int initialize(final IMbmsGroupCallSessionCallback callback, 47 final int subscriptionId) throws RemoteException { 48 if (callback == null) { 49 throw new NullPointerException("Callback must not be null"); 50 } 51 52 final int uid = Binder.getCallingUid(); 53 54 int result = MbmsGroupCallServiceBase.this.initialize( 55 new MbmsGroupCallSessionCallback() { 56 @Override 57 public void onError(final int errorCode, final String message) { 58 try { 59 if (errorCode == MbmsErrors.UNKNOWN) { 60 throw new IllegalArgumentException( 61 "Middleware cannot send an unknown error."); 62 } 63 callback.onError(errorCode, message); 64 } catch (RemoteException e) { 65 onAppCallbackDied(uid, subscriptionId); 66 } 67 } 68 69 @Override 70 public void onAvailableSaisUpdated(final List currentSais, 71 final List availableSais) { 72 try { 73 callback.onAvailableSaisUpdated(currentSais, availableSais); 74 } catch (RemoteException e) { 75 onAppCallbackDied(uid, subscriptionId); 76 } 77 } 78 79 @Override 80 public void onServiceInterfaceAvailable(final String interfaceName, 81 final int index) { 82 try { 83 callback.onServiceInterfaceAvailable(interfaceName, index); 84 } catch (RemoteException e) { 85 onAppCallbackDied(uid, subscriptionId); 86 } 87 } 88 89 @Override 90 public void onMiddlewareReady() { 91 try { 92 callback.onMiddlewareReady(); 93 } catch (RemoteException e) { 94 onAppCallbackDied(uid, subscriptionId); 95 } 96 } 97 }, subscriptionId); 98 99 if (result == MbmsErrors.SUCCESS) { 100 callback.asBinder().linkToDeath(new DeathRecipient() { 101 @Override 102 public void binderDied() { 103 onAppCallbackDied(uid, subscriptionId); 104 } 105 }, 0); 106 } 107 108 return result; 109 } 110 111 @Override 112 public void stopGroupCall(int subId, long tmgi) { 113 MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi); 114 } 115 116 @Override 117 public void updateGroupCall(int subscriptionId, long tmgi, List saiList, 118 List frequencyList) { 119 MbmsGroupCallServiceBase.this.updateGroupCall( 120 subscriptionId, tmgi, saiList, frequencyList); 121 } 122 123 @Override 124 public int startGroupCall(final int subscriptionId, final long tmgi, 125 final List saiList, 126 final List frequencyList, final IGroupCallCallback callback) 127 throws RemoteException { 128 if (callback == null) { 129 throw new NullPointerException("Callback must not be null"); 130 } 131 132 final int uid = Binder.getCallingUid(); 133 134 int result = MbmsGroupCallServiceBase.this.startGroupCall( 135 subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() { 136 @Override 137 public void onError(final int errorCode, final String message) { 138 try { 139 if (errorCode == MbmsErrors.UNKNOWN) { 140 throw new IllegalArgumentException( 141 "Middleware cannot send an unknown error."); 142 } 143 callback.onError(errorCode, message); 144 } catch (RemoteException e) { 145 onAppCallbackDied(uid, subscriptionId); 146 } 147 } 148 149 public void onGroupCallStateChanged(int state, int reason) { 150 try { 151 callback.onGroupCallStateChanged(state, reason); 152 } catch (RemoteException e) { 153 onAppCallbackDied(uid, subscriptionId); 154 } 155 } 156 157 public void onBroadcastSignalStrengthUpdated(int signalStrength) { 158 try { 159 callback.onBroadcastSignalStrengthUpdated(signalStrength); 160 } catch (RemoteException e) { 161 onAppCallbackDied(uid, subscriptionId); 162 } 163 } 164 }); 165 166 if (result == MbmsErrors.SUCCESS) { 167 callback.asBinder().linkToDeath(new DeathRecipient() { 168 @Override 169 public void binderDied() { 170 onAppCallbackDied(uid, subscriptionId); 171 } 172 }, 0); 173 } 174 175 return result; 176 } 177 178 @Override 179 public void dispose(int subId) throws RemoteException { 180 MbmsGroupCallServiceBase.this.dispose(subId); 181 } 182 }; 183 184 /** 185 * Initialize the group call service for this app and subscription ID, registering the callback. 186 * 187 * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which 188 * will be intercepted and passed to the app as 189 * {@link MbmsErrors.InitializationErrtrors#ERROR_UNABLE_TO_INITIALIZE} 190 * 191 * May return any value from {@link MbmsErrors.InitializationErrors} 192 * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via 193 * {@link IMbmsGroupCallSessionCallback#onError(int, String)}. 194 * 195 * @param callback The callback to use to communicate with the app. 196 * @param subscriptionId The subscription ID to use. 197 */ initialize(@onNull MbmsGroupCallSessionCallback callback, int subscriptionId)198 public int initialize(@NonNull MbmsGroupCallSessionCallback callback, int subscriptionId) 199 throws RemoteException { 200 throw new UnsupportedOperationException("Not implemented"); 201 } 202 203 /** 204 * Starts a particular group call. This method may perform asynchronous work. When 205 * the call is ready for consumption, the middleware should inform the app via 206 * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}. 207 * 208 * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} 209 * 210 * @param subscriptionId The subscription id to use. 211 * @param tmgi The TMGI, an identifier for the group call. 212 * @param saiList A list of SAIs for the group call. 213 * @param frequencyList A list of frequencies for the group call. 214 * @param callback The callback object on which the app wishes to receive updates. 215 * @return Any error in {@link MbmsErrors.GeneralErrors} 216 */ startGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList, @NonNull List<Integer> frequencyList, @NonNull GroupCallCallback callback)217 public int startGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList, 218 @NonNull List<Integer> frequencyList, @NonNull GroupCallCallback callback) { 219 throw new UnsupportedOperationException("Not implemented"); 220 } 221 222 /** 223 * Stop the group call identified by {@code tmgi}. 224 * 225 * The callback provided via {@link #startGroupCall} should no longer be 226 * used after this method has called by the app. 227 * 228 * May throw an {@link IllegalStateException} 229 * 230 * @param subscriptionId The subscription id to use. 231 * @param tmgi The TMGI for the call to stop. 232 */ stopGroupCall(int subscriptionId, long tmgi)233 public void stopGroupCall(int subscriptionId, long tmgi) { 234 throw new UnsupportedOperationException("Not implemented"); 235 } 236 237 /** 238 * Called when the app receives new SAI and frequency information for the group call identified 239 * by {@code tmgi}. 240 * @param saiList New list of SAIs that the call is available on. 241 * @param frequencyList New list of frequencies that the call is available on. 242 */ updateGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList, @NonNull List<Integer> frequencyList)243 public void updateGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList, 244 @NonNull List<Integer> frequencyList) { 245 throw new UnsupportedOperationException("Not implemented"); 246 } 247 248 /** 249 * Signals that the app wishes to dispose of the session identified by the 250 * {@code subscriptionId} argument and the caller's uid. No notification back to the 251 * app is required for this operation, and the corresponding callback provided via 252 * {@link #initialize} should no longer be used 253 * after this method has been called by the app. 254 * 255 * May throw an {@link IllegalStateException} 256 * 257 * @param subscriptionId The subscription id to use. 258 */ dispose(int subscriptionId)259 public void dispose(int subscriptionId) throws RemoteException { 260 throw new UnsupportedOperationException("Not implemented"); 261 } 262 263 /** 264 * Indicates that the app identified by the given UID and subscription ID has died. 265 * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}. 266 * @param subscriptionId The subscription ID the app is using. 267 */ onAppCallbackDied(int uid, int subscriptionId)268 public void onAppCallbackDied(int uid, int subscriptionId) { 269 } 270 271 @Override onBind(Intent intent)272 public IBinder onBind(Intent intent) { 273 return mInterface; 274 } 275 } 276