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