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