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