/* * 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 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.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.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 java.io.FileDescriptor; import java.io.PrintWriter; 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_DATA_ATTACHED = 500; private static final int EVENT_DATA_DETACHED = 600; private static DctController sDctController; private int mPhoneNum; private PhoneProxy[] mPhones; private DcSwitchStateMachine[] mDcSwitchStateMachine; private DcSwitchAsyncChannel[] mDcSwitchAsyncChannel; private Handler[] mDcSwitchStateHandler; private HashMap mRequestInfos = new HashMap(); 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() { onSubInfoReady(); } }; private ContentObserver mObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { logd("Settings change"); onSettingsChange(); } }; 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); 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; } 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_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; default: loge("Un-handled message [" + msg.what + "]"); } } private int requestNetwork(NetworkRequest request, int priority) { logd("requestNetwork request=" + request + ", priority=" + priority); RequestInfo requestInfo = new RequestInfo(request, priority); 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); 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)); } 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 int phoneId = getTopPriorityRequestPhoneId(); int activePhoneId = -1; for (int i=0; i iterator = mRequestInfos.keySet().iterator(); while (iterator.hasNext()) { RequestInfo requestInfo = mRequestInfos.get(iterator.next()); String specifier = requestInfo.request.networkCapabilities.getNetworkSpecifier(); if (specifier == null || specifier.equals("")) { if (requestInfo.executed) { String apn = apnForNetworkRequest(requestInfo.request); logd("[setDataSubId] activePhoneId:" + activePhoneId + ", subId =" + dataSubId); PhoneBase phoneBase = (PhoneBase)mPhones[activePhoneId].getActivePhone(); DcTrackerBase dcTracker = phoneBase.mDcTracker; dcTracker.decApnRefCount(apn); requestInfo.executed = false; } } } } // 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() { RequestInfo retRequestInfo = null; int phoneId = 0; int priority = -1; //TODO: Handle SIM Switch for (int i=0; i iterator = mRequestInfos.keySet().iterator(); while (iterator.hasNext()) { RequestInfo requestInfo = mRequestInfos.get(iterator.next()); logd("selectExecPhone requestInfo = " + requestInfo); if (getRequestPhoneId(requestInfo.request) == i && priority < requestInfo.priority) { priority = requestInfo.priority; retRequestInfo = requestInfo; } } } if (retRequestInfo != null) { phoneId = getRequestPhoneId(retRequestInfo.request); } logd("getTopPriorityRequestPhoneId = " + phoneId + ", priority = " + priority); 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 = null; loge("EIMS APN type not yet supported"); } if (error) { 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); if (!SubscriptionManager.isValidPhoneId(phoneId)) { phoneId = 0; if (!SubscriptionManager.isValidPhoneId(phoneId)) { throw new RuntimeException("Should not happen, no valid phoneId"); } } 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 mPendingReq = new SparseArray(); private Phone mPhone; public TelephonyNetworkFactory(Looper l, Context c, String TAG, Phone phone, NetworkCapabilities nc) { super(l, c, TAG, nc); mPhone = phone; log("NetworkCapabilities: " + nc); } @Override protected void needNetworkFor(NetworkRequest networkRequest, int score) { // figure out the apn type and enable it log("Cellular needs Network for " + networkRequest); if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) { log("Sub Info has not been ready, pending request."); mPendingReq.put(networkRequest.requestId, networkRequest); return; } if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) { DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker; String apn = apnForNetworkRequest(networkRequest); if (dcTracker.isApnSupported(apn)) { requestNetwork(networkRequest, dcTracker.getApnPriority(apn)); } else { log("Unsupported APN"); } } else { log("Request not send, put to pending"); mPendingReq.put(networkRequest.requestId, networkRequest); } } @Override protected void releaseNetworkFor(NetworkRequest networkRequest) { log("Cellular releasing Network for " + networkRequest); if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) { log("Sub Info has not been ready, remove request."); mPendingReq.remove(networkRequest.requestId); return; } if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) { DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker; String apn = apnForNetworkRequest(networkRequest); if (dcTracker.isApnSupported(apn)) { releaseNetwork(networkRequest); } else { log("Unsupported APN"); } } else { log("Request not release"); } } @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); } } } 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 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) { pw.println(" " + tnf); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); pw.flush(); } }