blob: f4caf9e8d00b6c6cba7a3f6213704f8d44bafe7d [file] [log] [blame]
/*
* Copyright (C) 2014 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.dataconnection;
import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;
import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
import android.content.Context;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkRequest;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.provider.Settings;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseArray;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
public class DctController extends Handler {
private static final String LOG_TAG = "DctController";
private static final boolean DBG = true;
private static final int EVENT_PROCESS_REQUESTS = 100;
private static final int EVENT_EXECUTE_REQUEST = 101;
private static final int EVENT_EXECUTE_ALL_REQUESTS = 102;
private static final int EVENT_RELEASE_REQUEST = 103;
private static final int EVENT_RELEASE_ALL_REQUESTS = 104;
private static final int EVENT_RETRY_ATTACH = 105;
private static final int EVENT_SETTINGS_CHANGED = 106;
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 107;
private static final int EVENT_DATA_ATTACHED = 500;
private static final int EVENT_DATA_DETACHED = 600;
private static final int EVENT_EMERGENCY_CALL_TOGGLED = 700;
private static DctController sDctController;
private int mPhoneNum;
private PhoneProxy[] mPhones;
private DcSwitchStateMachine[] mDcSwitchStateMachine;
private DcSwitchAsyncChannel[] mDcSwitchAsyncChannel;
private Handler[] mDcSwitchStateHandler;
private HashMap<Integer, RequestInfo> mRequestInfos = new HashMap<Integer, RequestInfo>();
private Context mContext;
/** Used to send us NetworkRequests from ConnectivityService. Remember it so we can
* unregister on dispose. */
private Messenger[] mNetworkFactoryMessenger;
private NetworkFactory[] mNetworkFactory;
private NetworkCapabilities[] mNetworkFilter;
private SubscriptionController mSubController = SubscriptionController.getInstance();
private SubscriptionManager mSubMgr;
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
DctController.this.obtainMessage(EVENT_SUBSCRIPTIONS_CHANGED).sendToTarget();
}
};
private ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
logd("Settings change");
DctController.this.obtainMessage(EVENT_SETTINGS_CHANGED).sendToTarget();
}
};
public void updatePhoneObject(PhoneProxy phone) {
if (phone == null) {
loge("updatePhoneObject phone = null");
return;
}
PhoneBase phoneBase = (PhoneBase)phone.getActivePhone();
if (phoneBase == null) {
loge("updatePhoneObject phoneBase = null");
return;
}
for (int i = 0; i < mPhoneNum; i++) {
if (mPhones[i] == phone) {
updatePhoneBaseForIndex(i, phoneBase);
break;
}
}
}
private void updatePhoneBaseForIndex(int index, PhoneBase phoneBase) {
logd("updatePhoneBaseForIndex for phone index=" + index);
phoneBase.getServiceStateTracker().registerForDataConnectionAttached(mRspHandler,
EVENT_DATA_ATTACHED + index, null);
phoneBase.getServiceStateTracker().registerForDataConnectionDetached(mRspHandler,
EVENT_DATA_DETACHED + index, null);
phoneBase.registerForEmergencyCallToggle(mRspHandler,
EVENT_EMERGENCY_CALL_TOGGLED + index, null);
ConnectivityManager cm = (ConnectivityManager)mPhones[index].getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (mNetworkFactoryMessenger != null) {
logd("unregister TelephonyNetworkFactory for phone index=" + index);
cm.unregisterNetworkFactory(mNetworkFactoryMessenger[index]);
mNetworkFactoryMessenger[index] = null;
mNetworkFactory[index] = null;
mNetworkFilter[index] = null;
}
// TODO - just make this a singleton. It'll be simpler
mNetworkFilter[index] = new NetworkCapabilities();
mNetworkFilter[index].addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
mNetworkFactory[index] = new TelephonyNetworkFactory(this.getLooper(),
mPhones[index].getContext(), "TelephonyNetworkFactory", phoneBase,
mNetworkFilter[index]);
mNetworkFactory[index].setScoreFilter(50);
mNetworkFactoryMessenger[index] = new Messenger(mNetworkFactory[index]);
cm.registerNetworkFactory(mNetworkFactoryMessenger[index], "Telephony");
}
private Handler mRspHandler = new Handler() {
@Override
public void handleMessage(Message msg){
if (msg.what >= EVENT_EMERGENCY_CALL_TOGGLED) {
logd("EVENT_PHONE" + (msg.what - EVENT_EMERGENCY_CALL_TOGGLED + 1)
+ "_EMERGENCY_CALL_END.");
AsyncResult ar = (AsyncResult) msg.obj;
Integer toggle = (Integer) ar.result;
mDcSwitchAsyncChannel[msg.what - EVENT_EMERGENCY_CALL_TOGGLED].
notifyEmergencyCallToggled(toggle.intValue());
} else if (msg.what >= EVENT_DATA_DETACHED) {
logd("EVENT_PHONE" + (msg.what - EVENT_DATA_DETACHED + 1)
+ "_DATA_DETACH.");
mDcSwitchAsyncChannel[msg.what - EVENT_DATA_DETACHED].notifyDataDetached();
} else if (msg.what >= EVENT_DATA_ATTACHED) {
logd("EVENT_PHONE" + (msg.what - EVENT_DATA_ATTACHED + 1)
+ "_DATA_ATTACH.");
mDcSwitchAsyncChannel[msg.what - EVENT_DATA_ATTACHED].notifyDataAttached();
}
}
};
public static DctController getInstance() {
if (sDctController == null) {
throw new RuntimeException(
"DctController.getInstance can't be called before makeDCTController()");
}
return sDctController;
}
public static DctController makeDctController(PhoneProxy[] phones) {
if (sDctController == null) {
logd("makeDctController: new DctController phones.length=" + phones.length);
sDctController = new DctController(phones);
}
logd("makeDctController: X sDctController=" + sDctController);
return sDctController;
}
private DctController(PhoneProxy[] phones) {
logd("DctController(): phones.length=" + phones.length);
if (phones == null || phones.length == 0) {
if (phones == null) {
loge("DctController(phones): UNEXPECTED phones=null, ignore");
} else {
loge("DctController(phones): UNEXPECTED phones.length=0, ignore");
}
return;
}
mPhoneNum = phones.length;
mPhones = phones;
mDcSwitchStateMachine = new DcSwitchStateMachine[mPhoneNum];
mDcSwitchAsyncChannel = new DcSwitchAsyncChannel[mPhoneNum];
mDcSwitchStateHandler = new Handler[mPhoneNum];
mNetworkFactoryMessenger = new Messenger[mPhoneNum];
mNetworkFactory = new NetworkFactory[mPhoneNum];
mNetworkFilter = new NetworkCapabilities[mPhoneNum];
for (int i = 0; i < mPhoneNum; ++i) {
int phoneId = i;
mDcSwitchStateMachine[i] = new DcSwitchStateMachine(mPhones[i],
"DcSwitchStateMachine-" + phoneId, phoneId);
mDcSwitchStateMachine[i].start();
mDcSwitchAsyncChannel[i] = new DcSwitchAsyncChannel(mDcSwitchStateMachine[i], phoneId);
mDcSwitchStateHandler[i] = new Handler();
int status = mDcSwitchAsyncChannel[i].fullyConnectSync(mPhones[i].getContext(),
mDcSwitchStateHandler[i], mDcSwitchStateMachine[i].getHandler());
if (status == AsyncChannel.STATUS_SUCCESSFUL) {
logd("DctController(phones): Connect success: " + i);
} else {
loge("DctController(phones): Could not connect to " + i);
}
// Register for radio state change
PhoneBase phoneBase = (PhoneBase)mPhones[i].getActivePhone();
updatePhoneBaseForIndex(i, phoneBase);
}
mContext = mPhones[0].getContext();
mSubMgr = SubscriptionManager.from(mContext);
mSubMgr.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
//Register for settings change.
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION),
false, mObserver);
}
public void dispose() {
logd("DctController.dispose");
for (int i = 0; i < mPhoneNum; ++i) {
ConnectivityManager cm = (ConnectivityManager)mPhones[i].getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
cm.unregisterNetworkFactory(mNetworkFactoryMessenger[i]);
mNetworkFactoryMessenger[i] = null;
}
mSubMgr.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
mContext.getContentResolver().unregisterContentObserver(mObserver);
}
@Override
public void handleMessage (Message msg) {
logd("handleMessage msg=" + msg);
switch (msg.what) {
case EVENT_PROCESS_REQUESTS:
onProcessRequest();
break;
case EVENT_EXECUTE_REQUEST:
onExecuteRequest((RequestInfo)msg.obj);
break;
case EVENT_EXECUTE_ALL_REQUESTS:
onExecuteAllRequests(msg.arg1);
break;
case EVENT_RELEASE_REQUEST:
onReleaseRequest((RequestInfo)msg.obj);
break;
case EVENT_RELEASE_ALL_REQUESTS:
onReleaseAllRequests(msg.arg1);
break;
case EVENT_RETRY_ATTACH:
onRetryAttach(msg.arg1);
break;
case EVENT_SETTINGS_CHANGED:
onSettingsChanged();
break;
case EVENT_SUBSCRIPTIONS_CHANGED:
onSubInfoReady();
break;
default:
loge("Un-handled message [" + msg.what + "]");
}
}
private int requestNetwork(NetworkRequest request, int priority, LocalLog l) {
logd("requestNetwork request=" + request + ", priority=" + priority);
l.log("Dctc.requestNetwork, priority=" + priority);
if (mRequestInfos.containsKey(request.requestId)) {
logd("requestNetwork replacing " + mRequestInfos.get(request.requestId));
// NOTE: executedPhoneId might be reset
}
RequestInfo requestInfo = new RequestInfo(request, priority, l);
mRequestInfos.put(request.requestId, requestInfo);
processRequests();
return PhoneConstants.APN_REQUEST_STARTED;
}
private int releaseNetwork(NetworkRequest request) {
RequestInfo requestInfo = mRequestInfos.get(request.requestId);
logd("releaseNetwork request=" + request + ", requestInfo=" + requestInfo);
if (requestInfo != null) requestInfo.log("DctController.releaseNetwork");
mRequestInfos.remove(request.requestId);
releaseRequest(requestInfo);
processRequests();
return PhoneConstants.APN_REQUEST_STARTED;
}
void processRequests() {
logd("processRequests");
sendMessage(obtainMessage(EVENT_PROCESS_REQUESTS));
}
void executeRequest(RequestInfo request) {
logd("executeRequest, request= " + request);
sendMessage(obtainMessage(EVENT_EXECUTE_REQUEST, request));
}
void executeAllRequests(int phoneId) {
logd("executeAllRequests, phone:" + phoneId);
sendMessage(obtainMessage(EVENT_EXECUTE_ALL_REQUESTS, phoneId,0));
}
void releaseRequest(RequestInfo request) {
logd("releaseRequest, request= " + request);
sendMessage(obtainMessage(EVENT_RELEASE_REQUEST, request));
}
void releaseAllRequests(int phoneId) {
logd("releaseAllRequests, phone:" + phoneId);
sendMessage(obtainMessage(EVENT_RELEASE_ALL_REQUESTS, phoneId, 0));
}
public void retryAttach(int phoneId) {
logd("retryAttach, phone:" + phoneId);
sendMessage(obtainMessage(EVENT_RETRY_ATTACH, phoneId, 0));
}
private void onProcessRequest() {
//process all requests
//1. Check all requests and find subscription of the top priority
// request
//2. Is current data allowed on the selected subscription
//2-1. If yes, execute all the requests of the sub
//2-2. If no, set data not allow on the current PS subscription
//2-2-1. Set data allow on the selected subscription
final int requestedPhoneId = getTopPriorityRequestPhoneId();
int activePhoneId = INVALID_PHONE_INDEX;
for (int i=0; i<mDcSwitchStateMachine.length; i++) {
if (!mDcSwitchAsyncChannel[i].isIdleSync()) {
activePhoneId = i;
break;
}
}
logd("onProcessRequest requestedPhoneId=" + requestedPhoneId
+ ", activePhoneId=" + activePhoneId);
if (requestedPhoneId == INVALID_PHONE_INDEX) {
// we have no network request - don't bother with this
return;
}
// if we have no active phones or the active phone is the desired, make requests
if (activePhoneId == INVALID_PHONE_INDEX || activePhoneId == requestedPhoneId) {
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
if (requestInfo.executedPhoneId != INVALID_PHONE_INDEX) continue;
if (getRequestPhoneId(requestInfo.request) == requestedPhoneId) {
mDcSwitchAsyncChannel[requestedPhoneId].connect(requestInfo);
}
}
} else {
// otherwise detatch so we can try connecting to the high-priority phone
mDcSwitchAsyncChannel[activePhoneId].disconnectAll();
}
}
private void onExecuteRequest(RequestInfo requestInfo) {
if (requestInfo.executedPhoneId == INVALID_PHONE_INDEX &&
mRequestInfos.containsKey(requestInfo.request.requestId)) {
logd("onExecuteRequest request=" + requestInfo);
requestInfo.log("DctController.onExecuteRequest");
String apn = apnForNetworkRequest(requestInfo.request);
final int phoneId = getRequestPhoneId(requestInfo.request);
requestInfo.executedPhoneId = phoneId;
PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
DcTrackerBase dcTracker = phoneBase.mDcTracker;
dcTracker.incApnRefCount(apn, requestInfo.getLog());
}
}
private void onExecuteAllRequests(int phoneId) {
logd("onExecuteAllRequests phoneId=" + phoneId);
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
if (getRequestPhoneId(requestInfo.request) == phoneId) {
onExecuteRequest(requestInfo);
}
}
}
private void onReleaseRequest(RequestInfo requestInfo) {
logd("onReleaseRequest request=" + requestInfo);
if (requestInfo != null) {
requestInfo.log("DctController.onReleaseRequest");
if (requestInfo.executedPhoneId != INVALID_PHONE_INDEX) {
String apn = apnForNetworkRequest(requestInfo.request);
int phoneId = requestInfo.executedPhoneId;
requestInfo.executedPhoneId = INVALID_PHONE_INDEX;
PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
DcTrackerBase dcTracker = phoneBase.mDcTracker;
dcTracker.decApnRefCount(apn, requestInfo.getLog());
}
}
}
private void onReleaseAllRequests(int phoneId) {
logd("onReleaseAllRequests phoneId=" + phoneId);
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
if (requestInfo.executedPhoneId == phoneId) {
onReleaseRequest(requestInfo);
}
}
}
private void onRetryAttach(int phoneId) {
final int topPriPhone = getTopPriorityRequestPhoneId();
logd("onRetryAttach phoneId=" + phoneId + " topPri phone = " + topPriPhone);
if (phoneId != INVALID_PHONE_INDEX && phoneId == topPriPhone) {
mDcSwitchAsyncChannel[phoneId].retryConnect();
}
}
private void onSettingsChanged() {
//Sub Selection
long dataSubId = mSubController.getDefaultDataSubId();
int activePhoneId = -1;
for (int i=0; i<mDcSwitchStateMachine.length; i++) {
if (!mDcSwitchAsyncChannel[i].isIdleSync()) {
activePhoneId = i;
break;
}
}
int[] subIds = SubscriptionManager.getSubId(activePhoneId);
if (subIds == null || subIds.length == 0) {
loge("onSettingsChange, subIds null or length 0 for activePhoneId " + activePhoneId);
return;
}
logd("onSettingsChange, data sub: " + dataSubId + ", active data sub: " + subIds[0]);
if (subIds[0] != dataSubId) {
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
String specifier = requestInfo.request.networkCapabilities.getNetworkSpecifier();
if (specifier == null || specifier.equals("")) {
onReleaseRequest(requestInfo);
}
}
}
// Some request maybe pending due to invalid settings
// Try to handle pending request when settings changed
for (int i = 0; i < mPhoneNum; ++i) {
((DctController.TelephonyNetworkFactory)mNetworkFactory[i]).evalPendingRequest();
}
processRequests();
}
private int getTopPriorityRequestPhoneId() {
String topSubId = null;
int priority = -1;
int subId;
for (RequestInfo requestInfo : mRequestInfos.values()) {
logd("getTopPriorityRequestPhoneId requestInfo=" + requestInfo);
if (requestInfo.priority > priority) {
priority = requestInfo.priority;
topSubId = requestInfo.request.networkCapabilities.getNetworkSpecifier();
}
}
if (TextUtils.isEmpty(topSubId)) {
subId = mSubController.getDefaultDataSubId();
} else {
subId = Integer.parseInt(topSubId);
}
final int phoneId = mSubController.getPhoneId(subId);
if (phoneId == DEFAULT_PHONE_INDEX) {
// that means there isn't a phone for the default sub
return INVALID_PHONE_INDEX;
}
return phoneId;
}
private void onSubInfoReady() {
logd("onSubInfoReady mPhoneNum=" + mPhoneNum);
for (int i = 0; i < mPhoneNum; ++i) {
int subId = mPhones[i].getSubId();
logd("onSubInfoReady handle pending requests subId=" + subId);
mNetworkFilter[i].setNetworkSpecifier(String.valueOf(subId));
((DctController.TelephonyNetworkFactory)mNetworkFactory[i]).evalPendingRequest();
}
processRequests();
}
private String apnForNetworkRequest(NetworkRequest nr) {
NetworkCapabilities nc = nr.networkCapabilities;
// For now, ignore the bandwidth stuff
if (nc.getTransportTypes().length > 0 &&
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
return null;
}
// in the near term just do 1-1 matches.
// TODO - actually try to match the set of capabilities
int type = -1;
String name = null;
boolean error = false;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_DEFAULT;
type = ConnectivityManager.TYPE_MOBILE;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_MMS;
type = ConnectivityManager.TYPE_MOBILE_MMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_SUPL;
type = ConnectivityManager.TYPE_MOBILE_SUPL;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_DUN;
type = ConnectivityManager.TYPE_MOBILE_DUN;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_FOTA;
type = ConnectivityManager.TYPE_MOBILE_FOTA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_IMS;
type = ConnectivityManager.TYPE_MOBILE_IMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_CBS;
type = ConnectivityManager.TYPE_MOBILE_CBS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_IA;
type = ConnectivityManager.TYPE_MOBILE_IA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
if (name != null) error = true;
name = null;
loge("RCS APN type not yet supported");
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
if (name != null) error = true;
name = null;
loge("XCAP APN type not yet supported");
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
if (name != null) error = true;
name = PhoneConstants.APN_TYPE_EMERGENCY;
type = ConnectivityManager.TYPE_MOBILE_EMERGENCY;
}
if (error) {
// TODO: If this error condition is removed, the framework's handling of
// NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
// say FOTA and INTERNET are marked as restricted. This is not how
// NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
loge("Multiple apn types specified in request - result is unspecified!");
}
if (type == -1 || name == null) {
loge("Unsupported NetworkRequest in Telephony: nr=" + nr);
return null;
}
return name;
}
private int getRequestPhoneId(NetworkRequest networkRequest) {
String specifier = networkRequest.networkCapabilities.getNetworkSpecifier();
int subId;
if (specifier == null || specifier.equals("")) {
subId = mSubController.getDefaultDataSubId();
} else {
subId = Integer.parseInt(specifier);
}
int phoneId = mSubController.getPhoneId(subId);
return phoneId;
}
private static void logd(String s) {
if (DBG) Rlog.d(LOG_TAG, s);
}
private static void loge(String s) {
if (DBG) Rlog.e(LOG_TAG, s);
}
private class TelephonyNetworkFactory extends NetworkFactory {
private final SparseArray<NetworkRequest> mPendingReq = new SparseArray<NetworkRequest>();
private Phone mPhone;
private class RequestLogger {
public NetworkRequest request;
public LocalLog log;
public RequestLogger(NetworkRequest r, LocalLog log) {
request = r;
this.log = log;
}
}
private static final int MAX_REQUESTS_LOGGED = 20;
private static final int MAX_LOG_LINES_PER_REQUEST = 50;
private ArrayDeque<RequestLogger> mRequestLogs = new ArrayDeque<RequestLogger>();
public TelephonyNetworkFactory(Looper l, Context c, String TAG, Phone phone,
NetworkCapabilities nc) {
super(l, c, TAG, nc);
mPhone = phone;
log("NetworkCapabilities: " + nc);
}
public LocalLog requestLog(int requestId, String l) {
synchronized(mRequestLogs) {
for (RequestLogger r : mRequestLogs) {
if (r.request.requestId == requestId) {
r.log.log(l);
return r.log;
}
}
}
return null;
}
private LocalLog addLogger(NetworkRequest request) {
synchronized(mRequestLogs) {
for (RequestLogger r : mRequestLogs) {
if (r.request.requestId == request.requestId) {
return r.log;
}
}
LocalLog l = new LocalLog(MAX_LOG_LINES_PER_REQUEST);
RequestLogger logger = new RequestLogger(request, l);
while (mRequestLogs.size() >= MAX_REQUESTS_LOGGED) {
mRequestLogs.removeFirst();
}
mRequestLogs.addLast(logger);
return l;
}
}
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
// figure out the apn type and enable it
log("Cellular needs Network for " + networkRequest);
final LocalLog l = addLogger(networkRequest);
if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId()) ||
getRequestPhoneId(networkRequest) != mPhone.getPhoneId()) {
final String str = "Request not useable, pending request.";
log(str);
l.log(str);
mPendingReq.put(networkRequest.requestId, networkRequest);
return;
}
DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
String apn = apnForNetworkRequest(networkRequest);
if (dcTracker.isApnSupported(apn)) {
requestNetwork(networkRequest, dcTracker.getApnPriority(apn), l);
} else {
final String str = "Unsupported APN";
log(str);
l.log(str);
}
}
@Override
protected void releaseNetworkFor(NetworkRequest networkRequest) {
String str = "Cellular releasing Network for ";
log(str + networkRequest);
final LocalLog l = requestLog(networkRequest.requestId, str);
if (mPendingReq.get(networkRequest.requestId) != null) {
str = "Sub Info has not been ready, remove request.";
log(str);
if (l != null) l.log(str);
mPendingReq.remove(networkRequest.requestId);
return;
}
releaseNetwork(networkRequest);
}
@Override
protected void log(String s) {
if (DBG) Rlog.d(LOG_TAG, "[TNF " + mPhone.getSubId() + "]" + s);
}
public void evalPendingRequest() {
log("evalPendingRequest, pending request size is " + mPendingReq.size());
int key = 0;
for(int i = 0; i < mPendingReq.size(); i++) {
key = mPendingReq.keyAt(i);
NetworkRequest request = mPendingReq.get(key);
log("evalPendingRequest: request = " + request);
mPendingReq.remove(request.requestId);
needNetworkFor(request, 0);
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(fd, writer, args);
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.increaseIndent();
pw.println("Pending Requests:");
pw.increaseIndent();
for (int i = 0; i < mPendingReq.size(); i++) {
NetworkRequest request = mPendingReq.valueAt(i);
pw.println(request);
}
pw.decreaseIndent();
pw.println("Request History:");
pw.increaseIndent();
synchronized(mRequestLogs) {
for (RequestLogger r : mRequestLogs) {
pw.println(r.request);
pw.increaseIndent();
r.log.dump(fd, pw, args);
pw.decreaseIndent();
}
}
pw.decreaseIndent();
pw.decreaseIndent();
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("DctController:");
try {
for (DcSwitchStateMachine dssm : mDcSwitchStateMachine) {
dssm.dump(fd, pw, args);
}
} catch (Exception e) {
e.printStackTrace();
}
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
try {
for (Entry<Integer, RequestInfo> entry : mRequestInfos.entrySet()) {
pw.println("mRequestInfos[" + entry.getKey() + "]=" + entry.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
pw.flush();
pw.println("TelephonyNetworkFactories:");
for (NetworkFactory tnf : mNetworkFactory) {
tnf.dump(fd, pw, args);
}
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
pw.flush();
}
}