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