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 com.android.phone.testapps.embmsmw; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.net.Uri; 22 import android.os.Binder; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.IBinder; 26 import android.telephony.mbms.MbmsErrors; 27 import android.telephony.mbms.MbmsStreamingSessionCallback; 28 import android.telephony.mbms.StreamingService; 29 import android.telephony.mbms.StreamingServiceCallback; 30 import android.telephony.mbms.StreamingServiceInfo; 31 import android.telephony.mbms.vendor.MbmsStreamingServiceBase; 32 import android.util.Log; 33 34 import com.android.internal.os.SomeArgs; 35 36 import java.util.Arrays; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 43 public class EmbmsTestStreamingService extends Service { 44 private static final Set<String> ALLOWED_PACKAGES = new HashSet<String>() {{ 45 add("com.android.phone.testapps.embmsfrontend"); 46 }}; 47 48 private static final String TAG = "EmbmsTestStreaming"; 49 50 private static final long INITIALIZATION_DELAY = 200; 51 private static final long SEND_SERVICE_LIST_DELAY = 300; 52 private static final long START_STREAMING_DELAY = 500; 53 54 private static final int SEND_STREAMING_SERVICES_LIST = 1; 55 56 private final Map<FrontendAppIdentifier, MbmsStreamingSessionCallback> mAppCallbacks = 57 new HashMap<>(); 58 59 private HandlerThread mHandlerThread; 60 private Handler mHandler; 61 private Handler.Callback mWorkerCallback = (msg) -> { 62 switch (msg.what) { 63 case SEND_STREAMING_SERVICES_LIST: 64 SomeArgs args = (SomeArgs) msg.obj; 65 FrontendAppIdentifier appKey = (FrontendAppIdentifier) args.arg1; 66 List<StreamingServiceInfo> services = (List) args.arg2; 67 MbmsStreamingSessionCallback appCallback = mAppCallbacks.get(appKey); 68 if (appCallback != null) { 69 appCallback.onStreamingServicesUpdated(services); 70 } 71 break; 72 } 73 return true; 74 }; 75 76 private final MbmsStreamingServiceBase mBinder = new MbmsStreamingServiceBase() { 77 @Override 78 public int initialize(MbmsStreamingSessionCallback callback, int subId) { 79 int packageUid = Binder.getCallingUid(); 80 String[] packageNames = getPackageManager().getPackagesForUid(packageUid); 81 if (packageNames == null) { 82 return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED; 83 } 84 boolean isUidAllowed = Arrays.stream(packageNames).anyMatch(ALLOWED_PACKAGES::contains); 85 if (!isUidAllowed) { 86 return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED; 87 } 88 89 mHandler.postDelayed(() -> { 90 FrontendAppIdentifier appKey = new FrontendAppIdentifier(packageUid, subId); 91 if (!mAppCallbacks.containsKey(appKey)) { 92 mAppCallbacks.put(appKey, callback); 93 } else { 94 callback.onError( 95 MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, ""); 96 return; 97 } 98 callback.onMiddlewareReady(); 99 }, INITIALIZATION_DELAY); 100 return MbmsErrors.SUCCESS; 101 } 102 103 @Override 104 public int requestUpdateStreamingServices(int subscriptionId, List<String> serviceClasses) { 105 FrontendAppIdentifier appKey = 106 new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId); 107 checkInitialized(appKey); 108 109 List<StreamingServiceInfo> serviceInfos = 110 StreamingServiceRepository.getStreamingServicesForClasses(serviceClasses); 111 112 SomeArgs args = SomeArgs.obtain(); 113 args.arg1 = appKey; 114 args.arg2 = serviceInfos; 115 116 mHandler.removeMessages(SEND_STREAMING_SERVICES_LIST); 117 mHandler.sendMessageDelayed( 118 mHandler.obtainMessage(SEND_STREAMING_SERVICES_LIST, args), 119 SEND_SERVICE_LIST_DELAY); 120 return MbmsErrors.SUCCESS; 121 } 122 123 @Override 124 public int startStreaming(int subscriptionId, String serviceId, 125 StreamingServiceCallback callback) { 126 FrontendAppIdentifier appKey = 127 new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId); 128 checkInitialized(appKey); 129 checkServiceExists(serviceId); 130 131 if (StreamStateTracker.getStreamingState(appKey, serviceId) == 132 StreamingService.STATE_STARTED) { 133 return MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM; 134 } 135 136 mHandler.postDelayed( 137 () -> StreamStateTracker.startStreaming(appKey, serviceId, callback, 138 StreamingService.REASON_BY_USER_REQUEST), 139 START_STREAMING_DELAY); 140 return MbmsErrors.SUCCESS; 141 } 142 143 @Override 144 public Uri getPlaybackUri(int subscriptionId, String serviceId) { 145 FrontendAppIdentifier appKey = 146 new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId); 147 checkInitialized(appKey); 148 checkServiceExists(serviceId); 149 150 Uri streamingUri = StreamingServiceRepository.getUriForService(serviceId); 151 if (streamingUri == null) { 152 throw new IllegalArgumentException("Invalid service ID"); 153 } 154 return streamingUri; 155 } 156 157 @Override 158 public void stopStreaming(int subscriptionId, String serviceId) { 159 FrontendAppIdentifier appKey = 160 new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId); 161 checkInitialized(appKey); 162 checkServiceExists(serviceId); 163 164 mHandler.post(() -> StreamStateTracker.stopStreaming(appKey, serviceId, 165 StreamingService.REASON_BY_USER_REQUEST)); 166 StreamStateTracker.dispose(appKey, serviceId); 167 } 168 169 @Override 170 public void dispose(int subscriptionId) { 171 FrontendAppIdentifier appKey = 172 new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId); 173 checkInitialized(appKey); 174 175 Log.i(TAG, "Disposing app with uid " + Binder.getCallingUid()); 176 StreamStateTracker.disposeAll(appKey); 177 mAppCallbacks.remove(appKey); 178 } 179 180 @Override 181 public void onAppCallbackDied(int uid, int subscriptionId) { 182 FrontendAppIdentifier appKey = new FrontendAppIdentifier(uid, subscriptionId); 183 184 Log.i(TAG, "Disposing app " + appKey + " due to binder death"); 185 StreamStateTracker.disposeAll(appKey); 186 mAppCallbacks.remove(appKey); 187 } 188 }; 189 190 @Override onDestroy()191 public void onDestroy() { 192 super.onCreate(); 193 mHandlerThread.quitSafely(); 194 logd("EmbmsTestStreamingService onDestroy"); 195 } 196 197 @Override onBind(Intent intent)198 public IBinder onBind(Intent intent) { 199 logd("EmbmsTestStreamingService onBind"); 200 mHandlerThread = new HandlerThread("EmbmsTestStreamingServiceWorker"); 201 mHandlerThread.start(); 202 mHandler = new Handler(mHandlerThread.getLooper(), mWorkerCallback); 203 return mBinder; 204 } 205 logd(String s)206 private static void logd(String s) { 207 Log.d(TAG, s); 208 } 209 checkInitialized(FrontendAppIdentifier appKey)210 private void checkInitialized(FrontendAppIdentifier appKey) { 211 if (!mAppCallbacks.containsKey(appKey)) { 212 throw new IllegalStateException("Not yet initialized"); 213 } 214 } 215 checkServiceExists(String serviceId)216 private void checkServiceExists(String serviceId) { 217 if (StreamingServiceRepository.getStreamingServiceInfoForId(serviceId) == null) { 218 throw new IllegalArgumentException("Invalid service ID"); 219 } 220 } 221 } 222