blob: ae7ede008ba7c5ecd9e46c6f6754d5ce3b017b6d [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.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.INetworkService;
import android.telephony.INetworkServiceCallback;
import android.telephony.NetworkRegistrationState;
import android.telephony.NetworkService;
import 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 {
private static final String TAG = NetworkRegistrationManager.class.getSimpleName();
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.Stub mServiceBinder;
private RegManagerDeathRecipient mDeathRecipient;
public NetworkRegistrationManager(int transportType, Phone phone) {
mTransportType = transportType;
mPhone = phone;
mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
Context.CARRIER_CONFIG_SERVICE);
bindService();
}
public boolean isServiceConnected() {
return (mServiceBinder != null) && (mServiceBinder.isBinderAlive());
}
public void unregisterForNetworkRegistrationStateChanged(Handler h) {
mRegStateChangeRegistrants.remove(h);
}
public void registerForNetworkRegistrationStateChanged(Handler h, int what, Object obj) {
logd("registerForNetworkRegistrationStateChanged");
Registrant r = new Registrant(h, what, obj);
mRegStateChangeRegistrants.addUnique(h, what, obj);
}
private final Map<NetworkRegStateCallback, Message> mCallbackTable = new Hashtable();
public void getNetworkRegistrationState(int domain, Message onCompleteMessage) {
if (onCompleteMessage == null) return;
logd("getNetworkRegistrationState domain " + domain);
if (!isServiceConnected()) {
logd("service not connected.");
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null,
new IllegalStateException("Service not connected."));
onCompleteMessage.sendToTarget();
return;
}
NetworkRegStateCallback callback = new NetworkRegStateCallback();
try {
mCallbackTable.put(callback, onCompleteMessage);
mServiceBinder.getNetworkRegistrationState(mPhone.getPhoneId(), domain, callback);
} catch (RemoteException e) {
Rlog.e(TAG, "getNetworkRegistrationState 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 connected.");
mServiceBinder = (INetworkService.Stub) service;
mDeathRecipient = new RegManagerDeathRecipient(name);
try {
mServiceBinder.linkToDeath(mDeathRecipient, 0);
mServiceBinder.createNetworkServiceProvider(mPhone.getPhoneId());
mServiceBinder.registerForNetworkRegistrationStateChanged(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("onServiceDisconnected " + name);
if (mServiceBinder != null) {
mServiceBinder.unlinkToDeath(mDeathRecipient, 0);
}
}
}
private class NetworkRegStateCallback extends INetworkServiceCallback.Stub {
@Override
public void onGetNetworkRegistrationStateComplete(
int result, NetworkRegistrationState state) {
logd("onGetNetworkRegistrationStateComplete result "
+ result + " state " + state);
Message onCompleteMessage = mCallbackTable.remove(this);
if (onCompleteMessage != null) {
onCompleteMessage.arg1 = result;
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, state, null);
onCompleteMessage.sendToTarget();
} else {
loge("onCompleteMessage is null");
}
}
@Override
public void onNetworkStateChanged() {
logd("onNetworkStateChanged");
mRegStateChangeRegistrants.notifyRegistrants();
}
}
private boolean bindService() {
Intent intent = new Intent(NetworkService.NETWORK_SERVICE_INTERFACE);
intent.setPackage(getPackageName());
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.
return mPhone.getContext().bindService(intent, new NetworkServiceConnection(),
Context.BIND_AUTO_CREATE);
} catch (SecurityException e) {
loge("bindService failed " + e);
return false;
}
}
private String getPackageName() {
String packageName;
int resourceId;
String carrierConfig;
switch (mTransportType) {
case TransportType.WWAN:
resourceId = com.android.internal.R.string.config_wwan_network_service_package;
carrierConfig = CarrierConfigManager
.KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
break;
case TransportType.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) {
// If carrier config overrides it, use the one from carrier config
packageName = b.getString(carrierConfig, packageName);
}
logd("Binding to packageName " + packageName + " for transport type"
+ mTransportType);
return packageName;
}
private static int logd(String msg) {
return Rlog.d(TAG, msg);
}
private static int loge(String msg) {
return Rlog.e(TAG, msg);
}
}