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