1 /*
2  * Copyright (C) 2017 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.Nullable;
20 import android.annotation.SystemApi;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.Binder;
24 import android.os.RemoteException;
25 import android.telephony.mbms.IMbmsStreamingSessionCallback;
26 import android.telephony.mbms.IStreamingServiceCallback;
27 import android.telephony.mbms.MbmsErrors;
28 import android.telephony.mbms.MbmsStreamingSessionCallback;
29 import android.telephony.mbms.StreamingService;
30 import android.telephony.mbms.StreamingServiceCallback;
31 import android.telephony.mbms.StreamingServiceInfo;
32 
33 import java.util.List;
34 
35 /**
36  * Base class for MBMS streaming services. The middleware should return an instance of this
37  * object from its {@link android.app.Service#onBind(Intent)} method.
38  * @hide
39  */
40 @SystemApi
41 public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
42     /**
43      * Initialize streaming service for this app and subId, registering the listener.
44      *
45      * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
46      * will be intercepted and passed to the app as
47      * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
48      *
49      * May return any value from {@link MbmsErrors.InitializationErrors}
50      * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
51      * {@link IMbmsStreamingSessionCallback#onError(int, String)}.
52      *
53      * @param callback The callback to use to communicate with the app.
54      * @param subscriptionId The subscription ID to use.
55      */
initialize(MbmsStreamingSessionCallback callback, int subscriptionId)56     public int initialize(MbmsStreamingSessionCallback callback, int subscriptionId)
57             throws RemoteException {
58         return 0;
59     }
60 
61     /**
62      * Actual AIDL implementation that hides the callback AIDL from the middleware.
63      * @hide
64      */
65     @Override
initialize(final IMbmsStreamingSessionCallback callback, final int subscriptionId)66     public final int initialize(final IMbmsStreamingSessionCallback callback,
67             final int subscriptionId) throws RemoteException {
68         if (callback == null) {
69             throw new NullPointerException("Callback must not be null");
70         }
71 
72         final int uid = Binder.getCallingUid();
73 
74         int result = initialize(new MbmsStreamingSessionCallback() {
75             @Override
76             public void onError(final int errorCode, final String message) {
77                 try {
78                     if (errorCode == MbmsErrors.UNKNOWN) {
79                         throw new IllegalArgumentException(
80                                 "Middleware cannot send an unknown error.");
81                     }
82                     callback.onError(errorCode, message);
83                 } catch (RemoteException e) {
84                     onAppCallbackDied(uid, subscriptionId);
85                 }
86             }
87 
88             @Override
89             public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
90                 try {
91                     callback.onStreamingServicesUpdated(services);
92                 } catch (RemoteException e) {
93                     onAppCallbackDied(uid, subscriptionId);
94                 }
95             }
96 
97             @Override
98             public void onMiddlewareReady() {
99                 try {
100                     callback.onMiddlewareReady();
101                 } catch (RemoteException e) {
102                     onAppCallbackDied(uid, subscriptionId);
103                 }
104             }
105         }, subscriptionId);
106 
107         if (result == MbmsErrors.SUCCESS) {
108             callback.asBinder().linkToDeath(new DeathRecipient() {
109                 @Override
110                 public void binderDied() {
111                     onAppCallbackDied(uid, subscriptionId);
112                 }
113             }, 0);
114         }
115 
116         return result;
117     }
118 
119 
120     /**
121      * Registers serviceClasses of interest with the appName/subId key.
122      * Starts async fetching data on streaming services of matching classes to be reported
123      * later via {@link IMbmsStreamingSessionCallback#onStreamingServicesUpdated(List)}
124      *
125      * Note that subsequent calls with the same uid and subId will replace
126      * the service class list.
127      *
128      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
129      *
130      * @param subscriptionId The subscription id to use.
131      * @param serviceClasses The service classes that the app wishes to get info on. The strings
132      *                       may contain arbitrary data as negotiated between the app and the
133      *                       carrier.
134      * @return {@link MbmsErrors#SUCCESS} or any of the errors in
135      * {@link MbmsErrors.GeneralErrors}
136      */
137     @Override
requestUpdateStreamingServices(int subscriptionId, List<String> serviceClasses)138     public int requestUpdateStreamingServices(int subscriptionId,
139             List<String> serviceClasses) throws RemoteException {
140         return 0;
141     }
142 
143     /**
144      * Starts streaming on a particular service. This method may perform asynchronous work. When
145      * the middleware is ready to send bits to the frontend, it should inform the app via
146      * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
147      *
148      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
149      *
150      * @param subscriptionId The subscription id to use.
151      * @param serviceId The ID of the streaming service that the app has requested.
152      * @param callback The callback object on which the app wishes to receive updates.
153      * @return Any error in {@link MbmsErrors.GeneralErrors}
154      */
startStreaming(int subscriptionId, String serviceId, StreamingServiceCallback callback)155     public int startStreaming(int subscriptionId, String serviceId,
156             StreamingServiceCallback callback) throws RemoteException {
157         return 0;
158     }
159 
160     /**
161      * Actual AIDL implementation of startStreaming that hides the callback AIDL from the
162      * middleware.
163      * @hide
164      */
165     @Override
startStreaming(final int subscriptionId, String serviceId, final IStreamingServiceCallback callback)166     public int startStreaming(final int subscriptionId, String serviceId,
167             final IStreamingServiceCallback callback) throws RemoteException {
168         if (callback == null) {
169             throw new NullPointerException("Callback must not be null");
170         }
171 
172         final int uid = Binder.getCallingUid();
173 
174         int result = startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
175             @Override
176             public void onError(final int errorCode, final String message) {
177                 try {
178                     if (errorCode == MbmsErrors.UNKNOWN) {
179                         throw new IllegalArgumentException(
180                                 "Middleware cannot send an unknown error.");
181                     }
182                     callback.onError(errorCode, message);
183                 } catch (RemoteException e) {
184                     onAppCallbackDied(uid, subscriptionId);
185                 }
186             }
187 
188             @Override
189             public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
190                     @StreamingService.StreamingStateChangeReason final int reason) {
191                 try {
192                     callback.onStreamStateUpdated(state, reason);
193                 } catch (RemoteException e) {
194                     onAppCallbackDied(uid, subscriptionId);
195                 }
196             }
197 
198             @Override
199             public void onMediaDescriptionUpdated() {
200                 try {
201                     callback.onMediaDescriptionUpdated();
202                 } catch (RemoteException e) {
203                     onAppCallbackDied(uid, subscriptionId);
204                 }
205             }
206 
207             @Override
208             public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
209                 try {
210                     callback.onBroadcastSignalStrengthUpdated(signalStrength);
211                 } catch (RemoteException e) {
212                     onAppCallbackDied(uid, subscriptionId);
213                 }
214             }
215 
216             @Override
217             public void onStreamMethodUpdated(final int methodType) {
218                 try {
219                     callback.onStreamMethodUpdated(methodType);
220                 } catch (RemoteException e) {
221                     onAppCallbackDied(uid, subscriptionId);
222                 }
223             }
224         });
225 
226         if (result == MbmsErrors.SUCCESS) {
227             callback.asBinder().linkToDeath(new DeathRecipient() {
228                 @Override
229                 public void binderDied() {
230                     onAppCallbackDied(uid, subscriptionId);
231                 }
232             }, 0);
233         }
234 
235         return result;
236     }
237 
238     /**
239      * Retrieves the streaming URI for a particular service. If the middleware is not yet ready to
240      * stream the service, this method may return null.
241      *
242      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
243      *
244      * @param subscriptionId The subscription id to use.
245      * @param serviceId The ID of the streaming service that the app has requested.
246      * @return An opaque {@link Uri} to be passed to a video player that understands the format.
247      */
248     @Override
getPlaybackUri(int subscriptionId, String serviceId)249     public @Nullable Uri getPlaybackUri(int subscriptionId, String serviceId)
250             throws RemoteException {
251         return null;
252     }
253 
254     /**
255      * Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
256      * stream state change should be reported to the app via
257      * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
258      *
259      * In addition, the callback provided via
260      * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
261      * used after this method has called by the app.
262      *
263      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
264      *
265      * @param subscriptionId The subscription id to use.
266      * @param serviceId The ID of the streaming service that the app wishes to stop.
267      */
268     @Override
stopStreaming(int subscriptionId, String serviceId)269     public void stopStreaming(int subscriptionId, String serviceId)
270             throws RemoteException {
271     }
272 
273     /**
274      * Signals that the app wishes to dispose of the session identified by the
275      * {@code subscriptionId} argument and the caller's uid. No notification back to the
276      * app is required for this operation, and the corresponding callback provided via
277      * {@link #initialize(IMbmsStreamingSessionCallback, int)} should no longer be used
278      * after this method has been called by the app.
279      *
280      * May throw an {@link IllegalStateException}
281      *
282      * @param subscriptionId The subscription id to use.
283      */
284     @Override
dispose(int subscriptionId)285     public void dispose(int subscriptionId) throws RemoteException {
286     }
287 
288     /**
289      * Indicates that the app identified by the given UID and subscription ID has died.
290      * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
291      * @param subscriptionId The subscription ID the app is using.
292      */
onAppCallbackDied(int uid, int subscriptionId)293     public void onAppCallbackDied(int uid, int subscriptionId) {
294     }
295 
296 
297     // Following two methods exist to workaround b/124210145
298     /** @hide */
299     @SystemApi
300     @Override
asBinder()301     public android.os.IBinder asBinder() {
302         return super.asBinder();
303     }
304 
305     /** @hide */
306     @SystemApi
307     @Override
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)308     public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
309             int flags) throws RemoteException {
310         return super.onTransact(code, data, reply, flags);
311     }
312 }
313