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