blob: 7a9dd03709837980d328c8756e29a0974ca04f40 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.phone.testapps.embmsmw;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.mbms.IMbmsStreamingManagerCallback;
import android.telephony.mbms.MbmsException;
import android.telephony.mbms.MbmsStreamingManagerCallback;
import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.telephony.mbms.vendor.MbmsStreamingServiceBase;
import android.util.Log;
import com.android.internal.os.SomeArgs;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class EmbmsTestStreamingService extends Service {
private static final Set<String> ALLOWED_PACKAGES = new HashSet<String>() {{
add("com.android.phone.testapps.embmsfrontend");
}};
private static final String TAG = "EmbmsTestStreaming";
private static final long INITIALIZATION_DELAY = 200;
private static final long SEND_SERVICE_LIST_DELAY = 300;
private static final long START_STREAMING_DELAY = 500;
private static final int SEND_STREAMING_SERVICES_LIST = 1;
private final Map<FrontendAppIdentifier, MbmsStreamingManagerCallback> mAppCallbacks =
new HashMap<>();
private HandlerThread mHandlerThread;
private Handler mHandler;
private Handler.Callback mWorkerCallback = (msg) -> {
switch (msg.what) {
case SEND_STREAMING_SERVICES_LIST:
SomeArgs args = (SomeArgs) msg.obj;
FrontendAppIdentifier appKey = (FrontendAppIdentifier) args.arg1;
List<StreamingServiceInfo> services = (List) args.arg2;
MbmsStreamingManagerCallback appCallback = mAppCallbacks.get(appKey);
if (appCallback != null) {
appCallback.onStreamingServicesUpdated(services);
}
break;
}
return true;
};
private final MbmsStreamingServiceBase mBinder = new MbmsStreamingServiceBase() {
@Override
public int initialize(MbmsStreamingManagerCallback listener, int subId) {
int packageUid = Binder.getCallingUid();
String[] packageNames = getPackageManager().getPackagesForUid(packageUid);
if (packageNames == null) {
return MbmsException.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED;
}
boolean isUidAllowed = Arrays.stream(packageNames).anyMatch(ALLOWED_PACKAGES::contains);
if (!isUidAllowed) {
return MbmsException.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED;
}
mHandler.postDelayed(() -> {
FrontendAppIdentifier appKey = new FrontendAppIdentifier(packageUid, subId);
if (!mAppCallbacks.containsKey(appKey)) {
mAppCallbacks.put(appKey, listener);
} else {
listener.onError(
MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, "");
return;
}
listener.onMiddlewareReady();
}, INITIALIZATION_DELAY);
return MbmsException.SUCCESS;
}
@Override
public int getStreamingServices(int subscriptionId, List<String> serviceClasses) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
List<StreamingServiceInfo> serviceInfos =
StreamingServiceRepository.getStreamingServicesForClasses(serviceClasses);
SomeArgs args = SomeArgs.obtain();
args.arg1 = appKey;
args.arg2 = serviceInfos;
mHandler.removeMessages(SEND_STREAMING_SERVICES_LIST);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(SEND_STREAMING_SERVICES_LIST, args),
SEND_SERVICE_LIST_DELAY);
return MbmsException.SUCCESS;
}
@Override
public int startStreaming(int subscriptionId, String serviceId,
StreamingServiceCallback callback) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
checkServiceExists(serviceId);
if (StreamStateTracker.getStreamingState(appKey, serviceId) ==
StreamingService.STATE_STARTED) {
return MbmsException.StreamingErrors.ERROR_DUPLICATE_START_STREAM;
}
mHandler.postDelayed(
() -> StreamStateTracker.startStreaming(appKey, serviceId, callback,
StreamingService.REASON_BY_USER_REQUEST),
START_STREAMING_DELAY);
return MbmsException.SUCCESS;
}
@Override
public Uri getPlaybackUri(int subscriptionId, String serviceId) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
checkServiceExists(serviceId);
Uri streamingUri = StreamingServiceRepository.getUriForService(serviceId);
if (streamingUri == null) {
throw new IllegalArgumentException("Invalid service ID");
}
return streamingUri;
}
@Override
public void stopStreaming(int subscriptionId, String serviceId) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
checkServiceExists(serviceId);
mHandler.post(() -> StreamStateTracker.stopStreaming(appKey, serviceId,
StreamingService.REASON_BY_USER_REQUEST));
}
@Override
public void disposeStream(int subscriptionId, String serviceId) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
checkServiceExists(serviceId);
Log.i(TAG, "Disposing of stream " + serviceId);
StreamStateTracker.dispose(appKey, serviceId);
}
@Override
public void dispose(int subscriptionId) {
FrontendAppIdentifier appKey =
new FrontendAppIdentifier(Binder.getCallingUid(), subscriptionId);
checkInitialized(appKey);
Log.i(TAG, "Disposing app with uid " + Binder.getCallingUid());
StreamStateTracker.disposeAll(appKey);
mAppCallbacks.remove(appKey);
}
@Override
public void onAppCallbackDied(int uid, int subscriptionId) {
FrontendAppIdentifier appKey = new FrontendAppIdentifier(uid, subscriptionId);
Log.i(TAG, "Disposing app " + appKey + " due to binder death");
StreamStateTracker.disposeAll(appKey);
mAppCallbacks.remove(appKey);
}
};
@Override
public void onDestroy() {
super.onCreate();
mHandlerThread.quitSafely();
logd("EmbmsTestStreamingService onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
logd("EmbmsTestStreamingService onBind");
mHandlerThread = new HandlerThread("EmbmsTestStreamingServiceWorker");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mWorkerCallback);
return mBinder;
}
private static void logd(String s) {
Log.d(TAG, s);
}
private void checkInitialized(FrontendAppIdentifier appKey) {
if (!mAppCallbacks.containsKey(appKey)) {
throw new IllegalStateException("Not yet initialized");
}
}
private void checkServiceExists(String serviceId) {
if (StreamingServiceRepository.getStreamingServiceInfoForId(serviceId) == null) {
throw new IllegalArgumentException("Invalid service ID");
}
}
}