| /* |
| * Copyright (C) 2015 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 static android.telephony.PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE; |
| import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX; |
| import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.net.MatchAllNetworkSpecifier; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkFactory; |
| import android.net.NetworkRequest; |
| import android.net.NetworkSpecifier; |
| import android.net.StringNetworkSpecifier; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.os.RemoteException; |
| import android.telephony.PhoneCapability; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.Rlog; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.util.LocalLog; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.dataconnection.DcRequest; |
| import com.android.internal.util.IndentingPrintWriter; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Utility singleton to monitor subscription changes and incoming NetworkRequests |
| * and determine which phone/phones are active. |
| * |
| * Manages the ALLOW_DATA calls to modems and notifies phones about changes to |
| * the active phones. Note we don't wait for data attach (which may not happen anyway). |
| */ |
| public class PhoneSwitcher extends Handler { |
| private final static String LOG_TAG = "PhoneSwitcher"; |
| private final static boolean VDBG = false; |
| |
| private final List<DcRequest> mPrioritizedDcRequests = new ArrayList<DcRequest>(); |
| private final RegistrantList mActivePhoneRegistrants; |
| private final SubscriptionController mSubscriptionController; |
| private final int[] mPhoneSubscriptions; |
| private final CommandsInterface[] mCommandsInterfaces; |
| private final Context mContext; |
| private final PhoneState[] mPhoneStates; |
| private final int mNumPhones; |
| private final Phone[] mPhones; |
| private final LocalLog mLocalLog; |
| private final PhoneStateListener mPhoneStateListener; |
| |
| private int mMaxActivePhones; |
| private static PhoneSwitcher sPhoneSwitcher = null; |
| |
| // Default subscription ID from user setting. |
| private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| |
| // If mPreferredDataSubId is an active subscription, it overrides |
| // mDefaultDataSubId and decides: |
| // 1. In modem layer, which subscription is preferred to have data traffic on. |
| // 2. In TelephonyNetworkFactory, which subscription will apply default network requests, which |
| // are requests without specifying a subId. |
| private int mPreferredDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; |
| |
| @VisibleForTesting |
| // Corresponding phoneId after considerting mPreferredDataSubId and mDefaultDataSubId above. |
| protected int mPreferredDataPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; |
| |
| private static final int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 101; |
| private static final int EVENT_SUBSCRIPTION_CHANGED = 102; |
| private static final int EVENT_REQUEST_NETWORK = 103; |
| private static final int EVENT_RELEASE_NETWORK = 104; |
| private static final int EVENT_EMERGENCY_TOGGLE = 105; |
| private static final int EVENT_RADIO_CAPABILITY_CHANGED = 106; |
| private static final int EVENT_PREFERRED_SUBSCRIPTION_CHANGED = 107; |
| private static final int EVENT_RADIO_AVAILABLE = 108; |
| |
| // Depending on version of IRadioConfig, we need to send either RIL_REQUEST_ALLOW_DATA if it's |
| // 1.0, or RIL_REQUEST_SET_PREFERRED_DATA if it's 1.1 or later. So internally mHalCommandToUse |
| // will be either HAL_COMMAND_ALLOW_DATA or HAL_COMMAND_ALLOW_DATA or HAL_COMMAND_UNKNOWN. |
| private static final int HAL_COMMAND_UNKNOWN = 0; |
| private static final int HAL_COMMAND_ALLOW_DATA = 1; |
| private static final int HAL_COMMAND_PREFERRED_DATA = 2; |
| private int mHalCommandToUse = HAL_COMMAND_UNKNOWN; |
| |
| private RadioConfig mRadioConfig; |
| |
| private final static int MAX_LOCAL_LOG_LINES = 30; |
| |
| /** |
| * Method to get singleton instance. |
| */ |
| public static PhoneSwitcher getInstance() { |
| return sPhoneSwitcher; |
| } |
| |
| /** |
| * Method to create singleton instance. |
| */ |
| public static PhoneSwitcher make(int maxActivePhones, int numPhones, Context context, |
| SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr, |
| CommandsInterface[] cis, Phone[] phones) { |
| if (sPhoneSwitcher == null) { |
| sPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones, context, |
| subscriptionController, looper, tr, cis, phones); |
| } |
| |
| return sPhoneSwitcher; |
| } |
| |
| @VisibleForTesting |
| public PhoneSwitcher(Looper looper) { |
| super(looper); |
| mMaxActivePhones = 0; |
| mSubscriptionController = null; |
| mPhoneSubscriptions = null; |
| mCommandsInterfaces = null; |
| mContext = null; |
| mPhoneStates = null; |
| mPhones = null; |
| mLocalLog = null; |
| mActivePhoneRegistrants = null; |
| mNumPhones = 0; |
| mRadioConfig = RadioConfig.getInstance(mContext); |
| mPhoneStateListener = new PhoneStateListener(looper) { |
| public void onPhoneCapabilityChanged(PhoneCapability capability) { |
| onPhoneCapabilityChangedInternal(capability); |
| } |
| }; |
| } |
| |
| @VisibleForTesting |
| public PhoneSwitcher(int maxActivePhones, int numPhones, Context context, |
| SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr, |
| CommandsInterface[] cis, Phone[] phones) { |
| super(looper); |
| mContext = context; |
| mNumPhones = numPhones; |
| mPhones = phones; |
| mPhoneSubscriptions = new int[numPhones]; |
| mMaxActivePhones = maxActivePhones; |
| mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES); |
| |
| mSubscriptionController = subscriptionController; |
| mRadioConfig = RadioConfig.getInstance(mContext); |
| |
| mPhoneStateListener = new PhoneStateListener(looper) { |
| public void onPhoneCapabilityChanged(PhoneCapability capability) { |
| onPhoneCapabilityChangedInternal(capability); |
| } |
| }; |
| |
| TelephonyManager telephonyManager = |
| (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| telephonyManager.listen(mPhoneStateListener, LISTEN_PHONE_CAPABILITY_CHANGE); |
| |
| mActivePhoneRegistrants = new RegistrantList(); |
| mPhoneStates = new PhoneState[numPhones]; |
| for (int i = 0; i < numPhones; i++) { |
| mPhoneStates[i] = new PhoneState(); |
| if (mPhones[i] != null) { |
| mPhones[i].registerForEmergencyCallToggle(this, EVENT_EMERGENCY_TOGGLE, null); |
| } |
| } |
| |
| mCommandsInterfaces = cis; |
| |
| mCommandsInterfaces[0].registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); |
| try { |
| tr.addOnSubscriptionsChangedListener(context.getOpPackageName(), |
| mSubscriptionsChangedListener); |
| } catch (RemoteException e) { |
| } |
| |
| mContext.registerReceiver(mDefaultDataChangedReceiver, |
| new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)); |
| |
| NetworkCapabilities netCap = new NetworkCapabilities(); |
| netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| netCap.setNetworkSpecifier(new MatchAllNetworkSpecifier()); |
| |
| NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context, |
| netCap, this); |
| // we want to see all requests |
| networkFactory.setScoreFilter(101); |
| networkFactory.register(); |
| |
| log("PhoneSwitcher started"); |
| } |
| |
| private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| Message msg = PhoneSwitcher.this.obtainMessage(EVENT_DEFAULT_SUBSCRIPTION_CHANGED); |
| msg.sendToTarget(); |
| } |
| }; |
| |
| private final IOnSubscriptionsChangedListener mSubscriptionsChangedListener = |
| new IOnSubscriptionsChangedListener.Stub() { |
| @Override |
| public void onSubscriptionsChanged() { |
| Message msg = PhoneSwitcher.this.obtainMessage(EVENT_SUBSCRIPTION_CHANGED); |
| msg.sendToTarget(); |
| } |
| }; |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case EVENT_SUBSCRIPTION_CHANGED: { |
| onEvaluate(REQUESTS_UNCHANGED, "subChanged"); |
| break; |
| } |
| case EVENT_DEFAULT_SUBSCRIPTION_CHANGED: { |
| onEvaluate(REQUESTS_UNCHANGED, "defaultChanged"); |
| break; |
| } |
| case EVENT_REQUEST_NETWORK: { |
| onRequestNetwork((NetworkRequest)msg.obj); |
| break; |
| } |
| case EVENT_RELEASE_NETWORK: { |
| onReleaseNetwork((NetworkRequest)msg.obj); |
| break; |
| } |
| case EVENT_EMERGENCY_TOGGLE: { |
| onEvaluate(REQUESTS_CHANGED, "emergencyToggle"); |
| break; |
| } |
| case EVENT_RADIO_CAPABILITY_CHANGED: { |
| resendRilCommands(msg); |
| break; |
| } |
| case EVENT_PREFERRED_SUBSCRIPTION_CHANGED: { |
| onEvaluate(REQUESTS_UNCHANGED, "preferredDataSubIdChanged"); |
| break; |
| } |
| case EVENT_RADIO_AVAILABLE: { |
| updateHalCommandToUse(); |
| onEvaluate(REQUESTS_UNCHANGED, "EVENT_RADIO_AVAILABLE"); |
| break; |
| } |
| } |
| } |
| |
| private boolean isEmergency() { |
| for (Phone p : mPhones) { |
| if (p == null) continue; |
| if (p.isInEcm() || p.isInEmergencyCall()) return true; |
| } |
| return false; |
| } |
| |
| private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory { |
| private final PhoneSwitcher mPhoneSwitcher; |
| public PhoneSwitcherNetworkRequestListener (Looper l, Context c, |
| NetworkCapabilities nc, PhoneSwitcher ps) { |
| super(l, c, "PhoneSwitcherNetworkRequstListener", nc); |
| mPhoneSwitcher = ps; |
| } |
| |
| @Override |
| protected void needNetworkFor(NetworkRequest networkRequest, int score) { |
| if (VDBG) log("needNetworkFor " + networkRequest + ", " + score); |
| Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK); |
| msg.obj = networkRequest; |
| msg.sendToTarget(); |
| } |
| |
| @Override |
| protected void releaseNetworkFor(NetworkRequest networkRequest) { |
| if (VDBG) log("releaseNetworkFor " + networkRequest); |
| Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK); |
| msg.obj = networkRequest; |
| msg.sendToTarget(); |
| } |
| } |
| |
| private void onRequestNetwork(NetworkRequest networkRequest) { |
| final DcRequest dcRequest = new DcRequest(networkRequest, mContext); |
| if (mPrioritizedDcRequests.contains(dcRequest) == false) { |
| mPrioritizedDcRequests.add(dcRequest); |
| Collections.sort(mPrioritizedDcRequests); |
| onEvaluate(REQUESTS_CHANGED, "netRequest"); |
| } |
| } |
| |
| private void onReleaseNetwork(NetworkRequest networkRequest) { |
| final DcRequest dcRequest = new DcRequest(networkRequest, mContext); |
| |
| if (mPrioritizedDcRequests.remove(dcRequest)) { |
| onEvaluate(REQUESTS_CHANGED, "netReleased"); |
| } |
| } |
| |
| private static final boolean REQUESTS_CHANGED = true; |
| private static final boolean REQUESTS_UNCHANGED = false; |
| /** |
| * Re-evaluate things. |
| * Do nothing if nothing's changed. |
| * |
| * Otherwise, go through the requests in priority order adding their phone |
| * until we've added up to the max allowed. Then go through shutting down |
| * phones that aren't in the active phone list. Finally, activate all |
| * phones in the active phone list. |
| */ |
| private void onEvaluate(boolean requestsChanged, String reason) { |
| StringBuilder sb = new StringBuilder(reason); |
| if (isEmergency()) { |
| log("onEvalute aborted due to Emergency"); |
| return; |
| } |
| |
| // If we use HAL_COMMAND_PREFERRED_DATA, |
| boolean diffDetected = mHalCommandToUse != HAL_COMMAND_PREFERRED_DATA && requestsChanged; |
| |
| // Check if user setting of default data sub is changed. |
| final int dataSub = mSubscriptionController.getDefaultDataSubId(); |
| if (dataSub != mDefaultDataSubId) { |
| sb.append(" default ").append(mDefaultDataSubId).append("->").append(dataSub); |
| mDefaultDataSubId = dataSub; |
| } |
| |
| // Check if phoneId to subId mapping is changed. |
| for (int i = 0; i < mNumPhones; i++) { |
| int sub = mSubscriptionController.getSubIdUsingPhoneId(i); |
| if (sub != mPhoneSubscriptions[i]) { |
| sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]); |
| sb.append("->").append(sub); |
| mPhoneSubscriptions[i] = sub; |
| diffDetected = true; |
| } |
| } |
| |
| // Check if phoneId for preferred data is changed. |
| int oldPreferredDataPhoneId = mPreferredDataPhoneId; |
| updatePhoneIdForDefaultNetworkRequests(); |
| if (oldPreferredDataPhoneId != mPreferredDataPhoneId) { |
| sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId) |
| .append("->").append(mPreferredDataPhoneId); |
| diffDetected = true; |
| } |
| |
| if (diffDetected) { |
| log("evaluating due to " + sb.toString()); |
| if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) { |
| if (SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) { |
| mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, null); |
| } |
| } else { |
| List<Integer> newActivePhones = new ArrayList<Integer>(); |
| |
| /** |
| * If all phones can have PS attached, activate all. |
| * Otherwise, choose to activate phones according to requests. And |
| * if list is not full, add mPreferredDataPhoneId. |
| */ |
| if (mMaxActivePhones == mPhones.length) { |
| for (int i = 0; i < mMaxActivePhones; i++) { |
| newActivePhones.add(mPhones[i].mPhoneId); |
| } |
| } else { |
| for (DcRequest dcRequest : mPrioritizedDcRequests) { |
| int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); |
| if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; |
| if (newActivePhones.contains(phoneIdForRequest)) continue; |
| newActivePhones.add(phoneIdForRequest); |
| if (newActivePhones.size() >= mMaxActivePhones) break; |
| } |
| |
| if (newActivePhones.size() < mMaxActivePhones |
| && newActivePhones.contains(mPreferredDataPhoneId) |
| && SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) { |
| newActivePhones.add(mPreferredDataPhoneId); |
| } |
| } |
| |
| if (VDBG) { |
| log("default subId = " + mDefaultDataSubId); |
| log("preferred subId = " + mPreferredDataSubId); |
| for (int i = 0; i < mNumPhones; i++) { |
| log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]"); |
| } |
| log(" newActivePhones:"); |
| for (Integer i : newActivePhones) log(" " + i); |
| } |
| |
| for (int phoneId = 0; phoneId < mNumPhones; phoneId++) { |
| if (!newActivePhones.contains(phoneId)) { |
| deactivate(phoneId); |
| } |
| } |
| |
| // only activate phones up to the limit |
| for (int phoneId : newActivePhones) { |
| activate(phoneId); |
| } |
| } |
| // Notify all registrants. |
| mActivePhoneRegistrants.notifyRegistrants(); |
| } |
| } |
| |
| private static class PhoneState { |
| public volatile boolean active = false; |
| public long lastRequested = 0; |
| } |
| |
| private void activate(int phoneId) { |
| switchPhone(phoneId, true); |
| } |
| |
| private void deactivate(int phoneId) { |
| switchPhone(phoneId, false); |
| } |
| |
| private void switchPhone(int phoneId, boolean active) { |
| PhoneState state = mPhoneStates[phoneId]; |
| if (state.active == active) return; |
| state.active = active; |
| log((active ? "activate " : "deactivate ") + phoneId); |
| state.lastRequested = System.currentTimeMillis(); |
| if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| // Skip ALLOW_DATA for single SIM device |
| if (mNumPhones > 1) { |
| mCommandsInterfaces[phoneId].setDataAllowed(active, null); |
| } |
| } |
| } |
| |
| /** |
| * Used when the modem may have been rebooted and we |
| * want to resend setDataAllowed or setPreferredData |
| */ |
| public void onRadioCapChanged(int phoneId) { |
| validatePhoneId(phoneId); |
| Message msg = obtainMessage(EVENT_RADIO_CAPABILITY_CHANGED); |
| msg.arg1 = phoneId; |
| msg.sendToTarget(); |
| } |
| |
| private void resendRilCommands(Message msg) { |
| final int phoneId = msg.arg1; |
| if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| // Skip ALLOW_DATA for single SIM device |
| if (mNumPhones > 1) { |
| mCommandsInterfaces[phoneId].setDataAllowed(mPhoneStates[phoneId].active, null); |
| } |
| } else { |
| mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, null); |
| } |
| } |
| |
| private void onPhoneCapabilityChangedInternal(PhoneCapability capability) { |
| int newMaxActivePhones = TelephonyManager.getDefault() |
| .getNumberOfModemsWithSimultaneousDataConnections(); |
| if (mMaxActivePhones != newMaxActivePhones) { |
| mMaxActivePhones = newMaxActivePhones; |
| log("Max active phones changed to " + mMaxActivePhones); |
| onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged"); |
| } |
| } |
| |
| private int phoneIdForRequest(NetworkRequest netRequest) { |
| NetworkSpecifier specifier = netRequest.networkCapabilities.getNetworkSpecifier(); |
| if (specifier == null) { |
| return mPreferredDataPhoneId; |
| } |
| |
| int subId; |
| |
| if (specifier instanceof StringNetworkSpecifier) { |
| try { |
| subId = Integer.parseInt(((StringNetworkSpecifier) specifier).specifier); |
| } catch (NumberFormatException e) { |
| Rlog.e(LOG_TAG, "NumberFormatException on " |
| + ((StringNetworkSpecifier) specifier).specifier); |
| subId = INVALID_SUBSCRIPTION_ID; |
| } |
| } else { |
| subId = INVALID_SUBSCRIPTION_ID; |
| } |
| |
| int phoneId = INVALID_PHONE_INDEX; |
| if (subId == INVALID_SUBSCRIPTION_ID) return phoneId; |
| |
| for (int i = 0 ; i < mNumPhones; i++) { |
| if (mPhoneSubscriptions[i] == subId) { |
| phoneId = i; |
| break; |
| } |
| } |
| return phoneId; |
| } |
| |
| private int getSubIdForDefaultNetworkRequests() { |
| if (mSubscriptionController.isActiveSubId(mPreferredDataSubId)) { |
| return mPreferredDataSubId; |
| } else { |
| return mDefaultDataSubId; |
| } |
| } |
| |
| // This updates mPreferredDataPhoneId which decides which phone should |
| // handle default network requests. |
| private void updatePhoneIdForDefaultNetworkRequests() { |
| int subId = getSubIdForDefaultNetworkRequests(); |
| int phoneId = SubscriptionManager.INVALID_PHONE_INDEX; |
| |
| if (SubscriptionManager.isUsableSubIdValue(subId)) { |
| for (int i = 0; i < mNumPhones; i++) { |
| if (mPhoneSubscriptions[i] == subId) { |
| phoneId = i; |
| break; |
| } |
| } |
| } |
| |
| mPreferredDataPhoneId = phoneId; |
| } |
| |
| /** |
| * Returns whether phone should handle network requests |
| * that don't specify a subId. |
| */ |
| public boolean shouldApplyUnspecifiedRequests(int phoneId) { |
| validatePhoneId(phoneId); |
| if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) { |
| return phoneId == mPreferredDataPhoneId; |
| } else { |
| return mPhoneStates[phoneId].active && phoneId == mPreferredDataPhoneId; |
| } |
| } |
| |
| /** |
| * Returns whether phone should handle network requests |
| * that specify a subId. |
| */ |
| public boolean shouldApplySpecifiedRequests(int phoneId) { |
| validatePhoneId(phoneId); |
| // If we use SET_PREFERRED_DATA, always apply specified network requests. Otherwise, |
| // only apply network requests if the phone is active (dataAllowed). |
| return mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA || mPhoneStates[phoneId].active; |
| } |
| |
| /** |
| * If preferred phone changes, or phone activation status changes, registrants |
| * will be notified. |
| */ |
| public void registerForActivePhoneSwitch(Handler h, int what, Object o) { |
| Registrant r = new Registrant(h, what, o); |
| mActivePhoneRegistrants.add(r); |
| r.notifyRegistrant(); |
| } |
| |
| public void unregisterForActivePhoneSwitch(Handler h) { |
| mActivePhoneRegistrants.remove(h); |
| } |
| |
| private void validatePhoneId(int phoneId) { |
| if (phoneId < 0 || phoneId >= mNumPhones) { |
| throw new IllegalArgumentException("Invalid PhoneId"); |
| } |
| } |
| |
| /** |
| * Set a subscription as preferred data subscription. |
| * See {@link SubscriptionManager#setPreferredData(int)} for more details. |
| */ |
| public void setPreferredData(int subId) { |
| if (mPreferredDataSubId != subId) { |
| log("setPreferredData subId changed to " + subId); |
| mPreferredDataSubId = subId; |
| Message msg = PhoneSwitcher.this.obtainMessage(EVENT_PREFERRED_SUBSCRIPTION_CHANGED); |
| msg.sendToTarget(); |
| } |
| } |
| |
| private void updateHalCommandToUse() { |
| mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported() |
| ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA; |
| } |
| |
| private void log(String l) { |
| Rlog.d(LOG_TAG, l); |
| mLocalLog.log(l); |
| } |
| |
| public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { |
| final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); |
| pw.println("PhoneSwitcher:"); |
| Calendar c = Calendar.getInstance(); |
| for (int i = 0; i < mNumPhones; i++) { |
| PhoneState ps = mPhoneStates[i]; |
| c.setTimeInMillis(ps.lastRequested); |
| pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" + |
| (ps.lastRequested == 0 ? "never" : |
| String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c))); |
| } |
| pw.increaseIndent(); |
| mLocalLog.dump(fd, pw, args); |
| pw.decreaseIndent(); |
| } |
| } |