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
17package android.telephony.mbms.vendor;
18
19import android.annotation.Nullable;
20import android.annotation.SystemApi;
21import android.annotation.TestApi;
22import android.content.Intent;
23import android.net.Uri;
24import android.os.Binder;
25import android.os.RemoteException;
26import android.telephony.mbms.IMbmsStreamingSessionCallback;
27import android.telephony.mbms.IStreamingServiceCallback;
28import android.telephony.mbms.MbmsErrors;
29import android.telephony.mbms.MbmsStreamingSessionCallback;
30import android.telephony.mbms.StreamingService;
31import android.telephony.mbms.StreamingServiceCallback;
32import android.telephony.mbms.StreamingServiceInfo;
33
34import 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
43public 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     */
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
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
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     */
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
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
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
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
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     */
295    public void onAppCallbackDied(int uid, int subscriptionId) {
296    }
297}
298