Files
frameworks_base/telephony/java/android/telephony/MbmsStreamingManager.java
Hall Liu fa2115ec47 Changes to MbmsStreamingManager for test app
Add binding methods to MbmsStreamingManager
Add constants for synchronous binding exceptions

Test: testapp
Change-Id: Ie798c6008029c4201ec1bb3c98c11c4a949a048a
2017-05-05 15:43:03 -07:00

284 lines
10 KiB
Java

/*
* Copyright (C) 2016 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 android.telephony;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.mbms.IMbmsStreamingManagerCallback;
import android.telephony.mbms.IStreamingServiceCallback;
import android.telephony.mbms.MbmsException;
import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.Log;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/** @hide */
public class MbmsStreamingManager {
private interface ServiceListener {
void onServiceConnected();
void onServiceDisconnected();
}
private static final String LOG_TAG = "MbmsStreamingManager";
public static final String MBMS_STREAMING_SERVICE_ACTION =
"android.telephony.action.EmbmsStreaming";
private static final boolean DEBUG = true;
private static final int BIND_TIMEOUT_MS = 3000;
private IMbmsStreamingService mService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (service != null) {
Log.i(LOG_TAG, String.format("Connected to service %s", name));
synchronized (MbmsStreamingManager.this) {
mService = IMbmsStreamingService.Stub.asInterface(service);
mServiceListeners.forEach(ServiceListener::onServiceConnected);
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(LOG_TAG, String.format("Disconnected from service %s", name));
synchronized (MbmsStreamingManager.this) {
mService = null;
mServiceListeners.forEach(ServiceListener::onServiceDisconnected);
}
}
};
private List<ServiceListener> mServiceListeners = new LinkedList<>();
private IMbmsStreamingManagerCallback mCallbackToApp;
private final String mAppName;
private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */
private MbmsStreamingManager(Context context, IMbmsStreamingManagerCallback listener,
String streamingAppName, int subscriptionId) {
mContext = context;
mAppName = streamingAppName;
mCallbackToApp = listener;
mSubscriptionId = subscriptionId;
}
/**
* Create a new MbmsStreamingManager using the given subscription ID.
*
* Note that this call will bind a remote service and that may take a bit. This
* may throw an {@link MbmsException}, indicating errors that may happen during
* the initialization or binding process.
*
* @param context
* @param listener
* @param streamingAppName
* @param subscriptionId
* @return
*/
public static MbmsStreamingManager create(Context context,
IMbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
throws MbmsException {
MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
streamingAppName, subscriptionId);
manager.bindAndInitialize();
return manager;
}
/**
* Create a new MbmsStreamingManager using the system default data subscription ID.
*
* Note that this call will bind a remote service and that may take a bit. This
* may throw an IllegalArgumentException or RemoteException.
*/
public static MbmsStreamingManager create(Context context,
IMbmsStreamingManagerCallback listener, String streamingAppName)
throws MbmsException {
int subId = SubscriptionManager.getDefaultSubscriptionId();
MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
streamingAppName, subId);
manager.bindAndInitialize();
return manager;
}
/**
* Terminates this instance, ending calls to the registered listener. Also terminates
* any streaming services spawned from this instance.
*/
public synchronized void dispose() {
if (mService == null) {
// Ignore and return, assume already disposed.
return;
}
try {
mService.dispose(mAppName, mSubscriptionId);
} catch (RemoteException e) {
// Ignore for now
}
mService = null;
}
/**
* An inspection API to retrieve the list of streaming media currently be advertised.
* The results are returned asynchronously through the previously registered callback.
* serviceClasses lets the app filter on types of programming and is opaque data between
* the app and the carrier.
*
* Multiple calls replace the list of serviceClasses of interest.
*
* May throw an IllegalArgumentException or RemoteException.
*
* Synchronous responses include
* <li>SUCCESS</li>
* <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
*
* Asynchronous errors through the listener include any of the errors except
* <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li>
* <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
* <li>ERROR_MSDC_END_OF_SESSION</li>
*/
public int getStreamingServices(List<String> classList) {
return 0;
}
/**
* Starts streaming a requested service, reporting status to the indicated listener.
* Returns an object used to control that stream.
*
* May throw an IllegalArgumentException or RemoteException.
*
* Asynchronous errors through the listener include any of the errors
*/
public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
IStreamingServiceCallback listener) {
return null;
}
/**
* Lists all the services currently being streamed to the device by this application
* on this given subId. Results are returned asynchronously through the previously
* registered callback.
*
* May throw a RemoteException.
*
* The return value is a success/error-code with the following possible values:
* <li>SUCCESS</li>
* <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
*
* Asynchronous errors through the listener include any of the errors except
* <li>ERROR_UNABLED_TO_START_SERVICE</li>
* <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
* <li>ERROR_MSDC_END_OF_SESSION</li>
*
*/
public int getActiveStreamingServices() {
return 0;
}
private void bindAndInitialize() throws MbmsException {
// Query for the proper service
PackageManager packageManager = mContext.getPackageManager();
Intent queryIntent = new Intent();
queryIntent.setAction(MBMS_STREAMING_SERVICE_ACTION);
List<ResolveInfo> streamingServices = packageManager.queryIntentServices(queryIntent,
PackageManager.MATCH_SYSTEM_ONLY);
if (streamingServices == null || streamingServices.size() == 0) {
throw new MbmsException(
MbmsException.ERROR_NO_SERVICE_INSTALLED);
}
if (streamingServices.size() > 1) {
throw new MbmsException(
MbmsException.ERROR_MULTIPLE_SERVICES_INSTALLED);
}
// Kick off the binding, and synchronously wait until binding is complete
final CountDownLatch latch = new CountDownLatch(1);
ServiceListener bindListener = new ServiceListener() {
@Override
public void onServiceConnected() {
latch.countDown();
}
@Override
public void onServiceDisconnected() {
}
};
synchronized (this) {
mServiceListeners.add(bindListener);
}
Intent bindIntent = new Intent();
bindIntent.setComponent(streamingServices.get(0).getComponentInfo().getComponentName());
mContext.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);
// Remove the listener and call the initialization method through the interface.
synchronized (this) {
mServiceListeners.remove(bindListener);
if (mService == null) {
throw new MbmsException(MbmsException.ERROR_BIND_TIMEOUT_OR_FAILURE);
}
try {
int returnCode = mService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
} catch (RemoteException e) {
mService = null;
Log.e(LOG_TAG, "Service died before initialization");
throw new MbmsException(MbmsException.ERROR_INITIALIZATION_REMOTE_EXCEPTION);
}
}
}
private static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) {
long endTime = System.currentTimeMillis() + timeoutMs;
while (System.currentTimeMillis() < endTime) {
try {
l.await(timeoutMs, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// keep waiting
}
if (l.getCount() <= 0) {
return;
}
}
}
}