blob: 69f64f1bc4e341fb07bad29be1b2470be437f0ab [file] [log] [blame]
/*
* Copyright 2018 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.internal.telephony;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.INetworkService;
import android.telephony.INetworkServiceCallback;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import com.android.telephony.Rlog;
import java.util.Hashtable;
import java.util.Map;
/**
* Class that serves as the layer between NetworkService and ServiceStateTracker. It helps binding,
* sending request and registering for state change to NetworkService.
*/
public class NetworkRegistrationManager extends Handler {
private final String mTag;
private static final int EVENT_BIND_NETWORK_SERVICE = 1;
private final int mTransportType;
private final Phone mPhone;
private final CarrierConfigManager mCarrierConfigManager;
// Registrants who listens registration state change callback from this class.
private final RegistrantList mRegStateChangeRegistrants = new RegistrantList();
private INetworkService mINetworkService;
private RegManagerDeathRecipient mDeathRecipient;
private String mTargetBindingPackageName;
private NetworkServiceConnection mServiceConnection;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
&& mPhone.getPhoneId() == intent.getIntExtra(
CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
// We should wait for carrier config changed event because the target binding
// package name can come from the carrier config. Note that we still get this event
// even when SIM is absent.
logd("Carrier config changed. Try to bind network service.");
sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE);
}
}
};
public NetworkRegistrationManager(@TransportType int transportType, Phone phone) {
mTransportType = transportType;
mPhone = phone;
String tagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
? "C" : "I") + "-" + mPhone.getPhoneId();
mTag = "NRM" + tagSuffix;
mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
Context.CARRIER_CONFIG_SERVICE);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
try {
Context contextAsUser = phone.getContext().createPackageContextAsUser(
phone.getContext().getPackageName(), 0, UserHandle.ALL);
contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
null /* broadcastPermission */, null);
} catch (PackageManager.NameNotFoundException e) {
loge("Package name not found: " + e.getMessage());
}
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_BIND_NETWORK_SERVICE, null);
sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE);
}
/**
* Handle message events
*
* @param msg The message to handle
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_BIND_NETWORK_SERVICE:
rebindService();
break;
default:
loge("Unhandled event " + msg.what);
}
}
public boolean isServiceConnected() {
return (mINetworkService != null) && (mINetworkService.asBinder().isBinderAlive());
}
public void unregisterForNetworkRegistrationInfoChanged(Handler h) {
mRegStateChangeRegistrants.remove(h);
}
public void registerForNetworkRegistrationInfoChanged(Handler h, int what, Object obj) {
logd("registerForNetworkRegistrationInfoChanged");
mRegStateChangeRegistrants.addUnique(h, what, obj);
}
private final Map<NetworkRegStateCallback, Message> mCallbackTable = new Hashtable();
public void requestNetworkRegistrationInfo(@NetworkRegistrationInfo.Domain int domain,
Message onCompleteMessage) {
if (onCompleteMessage == null) return;
if (!isServiceConnected()) {
loge("service not connected. Domain = "
+ ((domain == NetworkRegistrationInfo.DOMAIN_CS) ? "CS" : "PS"));
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null,
new IllegalStateException("Service not connected."));
onCompleteMessage.sendToTarget();
return;
}
NetworkRegStateCallback callback = new NetworkRegStateCallback();
try {
mCallbackTable.put(callback, onCompleteMessage);
mINetworkService.requestNetworkRegistrationInfo(mPhone.getPhoneId(), domain, callback);
} catch (RemoteException e) {
loge("requestNetworkRegistrationInfo RemoteException " + e);
mCallbackTable.remove(callback);
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null, e);
onCompleteMessage.sendToTarget();
}
}
private class RegManagerDeathRecipient implements IBinder.DeathRecipient {
private final ComponentName mComponentName;
RegManagerDeathRecipient(ComponentName name) {
mComponentName = name;
}
@Override
public void binderDied() {
// TODO: try to restart the service.
logd("NetworkService(" + mComponentName + " transport type "
+ mTransportType + ") died.");
}
}
private class NetworkServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
logd("service " + name + " for transport "
+ AccessNetworkConstants.transportTypeToString(mTransportType)
+ " is now connected.");
mINetworkService = INetworkService.Stub.asInterface(service);
mDeathRecipient = new RegManagerDeathRecipient(name);
try {
service.linkToDeath(mDeathRecipient, 0);
mINetworkService.createNetworkServiceProvider(mPhone.getPhoneId());
mINetworkService.registerForNetworkRegistrationInfoChanged(mPhone.getPhoneId(),
new NetworkRegStateCallback());
} catch (RemoteException exception) {
// Remote exception means that the binder already died.
mDeathRecipient.binderDied();
logd("RemoteException " + exception);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
logd("service " + name + " for transport "
+ AccessNetworkConstants.transportTypeToString(mTransportType)
+ " is now disconnected.");
mTargetBindingPackageName = null;
}
}
private class NetworkRegStateCallback extends INetworkServiceCallback.Stub {
@Override
public void onRequestNetworkRegistrationInfoComplete(
int result, NetworkRegistrationInfo info) {
logd("onRequestNetworkRegistrationInfoComplete result: "
+ result + ", info: " + info);
Message onCompleteMessage = mCallbackTable.remove(this);
if (onCompleteMessage != null) {
onCompleteMessage.arg1 = result;
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj,
new NetworkRegistrationInfo(info), null);
onCompleteMessage.sendToTarget();
} else {
loge("onCompleteMessage is null");
}
}
@Override
public void onNetworkStateChanged() {
logd("onNetworkStateChanged");
mRegStateChangeRegistrants.notifyRegistrants();
}
}
private void unbindService() {
if (mINetworkService != null && mINetworkService.asBinder().isBinderAlive()) {
logd("unbinding service");
// Remove the network availability updater and then unbind the service.
try {
mINetworkService.removeNetworkServiceProvider(mPhone.getPhoneId());
} catch (RemoteException e) {
loge("Cannot remove data service provider. " + e);
}
}
if (mServiceConnection != null) {
mPhone.getContext().unbindService(mServiceConnection);
}
mINetworkService = null;
mServiceConnection = null;
mTargetBindingPackageName = null;
}
private void bindService(String packageName) {
if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
loge("can't bindService with invalid phone or phoneId.");
return;
}
if (TextUtils.isEmpty(packageName)) {
loge("Can't find the binding package");
return;
}
Intent intent = null;
String className = getClassName();
if (TextUtils.isEmpty(className)) {
intent = new Intent(NetworkService.SERVICE_INTERFACE);
intent.setPackage(packageName);
} else {
ComponentName cm = new ComponentName(packageName, className);
intent = new Intent(NetworkService.SERVICE_INTERFACE).setComponent(cm);
}
try {
// We bind this as a foreground service because it is operating directly on the SIM,
// and we do not want it subjected to power-savings restrictions while doing so.
logd("Trying to bind " + getPackageName() + " for transport "
+ AccessNetworkConstants.transportTypeToString(mTransportType));
mServiceConnection = new NetworkServiceConnection();
if (!mPhone.getContext().bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE)) {
loge("Cannot bind to the data service.");
return;
}
mTargetBindingPackageName = packageName;
} catch (SecurityException e) {
loge("bindService failed " + e);
}
}
private void rebindService() {
String packageName = getPackageName();
// Do nothing if no need to rebind.
if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
&& TextUtils.equals(packageName, mTargetBindingPackageName)) {
logd("Service " + packageName + " already bound or being bound.");
return;
}
unbindService();
bindService(packageName);
}
private String getPackageName() {
String packageName;
int resourceId;
String carrierConfig;
switch (mTransportType) {
case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
resourceId = com.android.internal.R.string.config_wwan_network_service_package;
carrierConfig = CarrierConfigManager
.KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
break;
case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
resourceId = com.android.internal.R.string.config_wlan_network_service_package;
carrierConfig = CarrierConfigManager
.KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
break;
default:
throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+ mTransportType);
}
// Read package name from resource overlay
packageName = mPhone.getContext().getResources().getString(resourceId);
PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
packageName = b.getString(carrierConfig, packageName);
}
return packageName;
}
private String getClassName() {
String className;
int resourceId;
String carrierConfig;
switch (mTransportType) {
case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
resourceId = com.android.internal.R.string.config_wwan_network_service_class;
carrierConfig = CarrierConfigManager
.KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
break;
case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
resourceId = com.android.internal.R.string.config_wlan_network_service_class;
carrierConfig = CarrierConfigManager
.KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
break;
default:
throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+ mTransportType);
}
// Read class name from resource overlay
className = mPhone.getContext().getResources().getString(resourceId);
PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
className = b.getString(carrierConfig, className);
}
return className;
}
private void logd(String msg) {
Rlog.d(mTag, msg);
}
private void loge(String msg) {
Rlog.e(mTag, msg);
}
}