| /* |
| * Copyright (C) 2020 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.vendor; |
| |
| import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; |
| import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX; |
| import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| import static android.telephony.TelephonyManager.RADIO_POWER_UNAVAILABLE; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkRequest; |
| import android.os.AsyncResult; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Registrant; |
| import android.os.SystemProperties; |
| import android.telephony.data.ApnSetting; |
| import android.telephony.Rlog; |
| import android.telephony.SubscriptionManager; |
| |
| import android.text.TextUtils; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.CallManager; |
| import com.android.internal.telephony.Call; |
| import com.android.internal.telephony.CommandsInterface; |
| import com.android.internal.telephony.dataconnection.DcRequest; |
| import com.android.internal.telephony.dataconnection.DataEnabledSettings; |
| import com.android.internal.telephony.GsmCdmaCall; |
| import com.android.internal.telephony.imsphone.ImsPhone; |
| import com.android.internal.telephony.imsphone.ImsPhoneCall; |
| import com.android.internal.telephony.ITelephonyRegistry; |
| import com.android.internal.telephony.IccCardConstants; |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.PhoneFactory; |
| import com.android.internal.telephony.PhoneSwitcher; |
| import com.android.internal.telephony.SubscriptionController; |
| import com.android.internal.telephony.TelephonyIntents; |
| |
| import java.lang.Integer; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.List; |
| import android.net.StringNetworkSpecifier; |
| import android.net.NetworkSpecifier; |
| |
| public class VendorPhoneSwitcher extends PhoneSwitcher { |
| |
| private final int MAX_CONNECT_FAILURE_COUNT = 5; |
| private final int[] mRetryArray = new int []{5,10,20,40,60}; |
| private int[] mAllowDataFailure; |
| private boolean[] mDdsRequestSent; |
| private boolean mManualDdsSwitch = false; |
| private int mDefaultDataPhoneId = -1; |
| private String [] mSimStates; |
| private List<Integer> mNewActivePhones; |
| private boolean mWaitForDetachResponse = false; |
| private DdsSwitchState mDdsSwitchState = DdsSwitchState.NONE; |
| private final int USER_INITIATED_SWITCH = 0; |
| private final int NONUSER_INITIATED_SWITCH = 1; |
| protected final String PROPERTY_TEMP_DDSSWITCH = "persist.vendor.radio.enable_temp_dds"; |
| protected final GsmCdmaCall[] mFgCsCalls; |
| protected final GsmCdmaCall[] mBgCsCalls; |
| protected final GsmCdmaCall[] mRiCsCalls; |
| protected final ImsPhone[] mImsPhones; |
| protected final ImsPhoneCall[] mFgImsCalls; |
| protected final ImsPhoneCall[] mBgImsCalls; |
| protected final ImsPhoneCall[] mRiImsCalls; |
| |
| private final int EVENT_ALLOW_DATA_FALSE_RESPONSE = 201; |
| private final int EVENT_ALLOW_DATA_TRUE_RESPONSE = 202; |
| private final int EVENT_DDS_SWITCH_RESPONSE = 203; |
| private final int EVENT_PREFERRED_SUB_VALID = 204; |
| |
| private enum DdsSwitchState { |
| NONE, REQUIRED, DONE |
| } |
| |
| public VendorPhoneSwitcher(int maxActivePhones, Context context, Looper looper) { |
| super (maxActivePhones, context, looper); |
| mAllowDataFailure = new int[mActiveModemCount]; |
| mDdsRequestSent = new boolean[mActiveModemCount]; |
| mSimStates = new String[mActiveModemCount]; |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); |
| mContext.registerReceiver(mSimStateIntentReceiver, filter); |
| |
| mImsPhones = new ImsPhone[mActiveModemCount]; |
| mFgCsCalls = new GsmCdmaCall[mActiveModemCount]; |
| mBgCsCalls = new GsmCdmaCall[mActiveModemCount]; |
| mRiCsCalls = new GsmCdmaCall[mActiveModemCount]; |
| mFgImsCalls = new ImsPhoneCall[mActiveModemCount]; |
| mBgImsCalls = new ImsPhoneCall[mActiveModemCount]; |
| mRiImsCalls = new ImsPhoneCall[mActiveModemCount]; |
| |
| for (int i=0; i < mActiveModemCount; i++) { |
| if (PhoneFactory.getPhone(i) != null) { |
| mFgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getForegroundCall(); |
| mBgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getBackgroundCall(); |
| mRiCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getRingingCall(); |
| } |
| mImsPhones[i] = (ImsPhone)PhoneFactory.getPhone(i).getImsPhone(); |
| if (mImsPhones[i] != null) { |
| mFgImsCalls[i] = mImsPhones[i].getForegroundCall(); |
| mBgImsCalls[i] = mImsPhones[i].getBackgroundCall(); |
| mRiImsCalls[i] = mImsPhones[i].getRingingCall(); |
| } |
| |
| mDdsRequestSent[i] = false; |
| } |
| } |
| |
| public static VendorPhoneSwitcher make(int maxActivePhones, Context context, Looper looper) { |
| if (sPhoneSwitcher == null) { |
| sPhoneSwitcher = new VendorPhoneSwitcher(maxActivePhones, context, looper); |
| } |
| |
| return (VendorPhoneSwitcher)sPhoneSwitcher; |
| } |
| |
| private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { |
| String value = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); |
| int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, |
| SubscriptionManager.INVALID_PHONE_INDEX); |
| log("mSimStateIntentReceiver: phoneId = " + phoneId + " value = " + value); |
| if (SubscriptionManager.isValidPhoneId(phoneId)) { |
| mSimStates[phoneId] = value; |
| // If SIM is absent, allow DDS request always, which avoids DDS switch |
| // can't be completed in the no-SIM case because the sent status of the |
| // old preferred phone has no chance to reset in hot-swap |
| if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(value)) { |
| mDdsRequestSent[phoneId] = false; |
| } |
| } |
| |
| if (isSimReady(phoneId) && (getConnectFailureCount(phoneId) > 0)) { |
| sendRilCommands(phoneId); |
| } |
| } |
| } |
| }; |
| |
| @Override |
| public void handleMessage(Message msg) { |
| final int ddsSubId = mSubscriptionController.getDefaultDataSubId(); |
| final int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId); |
| |
| log("handle event - " + msg.what); |
| AsyncResult ar = null; |
| switch (msg.what) { |
| case EVENT_SUBSCRIPTION_CHANGED: { |
| if (mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| log("EVENT_SUBSCRIPTION_CHANGED: update HAL command"); |
| mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported() |
| ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA; |
| } |
| onEvaluate(REQUESTS_UNCHANGED, "subChanged"); |
| break; |
| } |
| case EVENT_PRECISE_CALL_STATE_CHANGED: { |
| log("EVENT_PRECISE_CALL_STATE_CHANGED"); |
| if (!isAnyVoiceCallActiveOnDevice()) { |
| for (int i = 0; i < mActiveModemCount; i++) { |
| if ((getConnectFailureCount(i) > 0) && |
| isPhoneIdValidForRetry(i)) { |
| sendRilCommands(i); |
| break; |
| } |
| } |
| } |
| super.handleMessage(msg); |
| break; |
| } |
| case EVENT_ALLOW_DATA_TRUE_RESPONSE: { |
| log("EVENT_ALLOW_DATA_TRUE_RESPONSE"); |
| onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj); |
| break; |
| } |
| case EVENT_ALLOW_DATA_FALSE_RESPONSE: { |
| log("EVENT_ALLOW_DATA_FALSE_RESPONSE"); |
| mWaitForDetachResponse = false; |
| if (mNewActivePhones != null) { |
| for (int phoneId : mNewActivePhones) { |
| activate(phoneId); |
| } |
| if (mNewActivePhones.contains(ddsPhoneId)) { |
| mManualDdsSwitch = false; |
| } |
| } else { |
| log("mNewActivePhones is NULL"); |
| } |
| break; |
| } |
| case EVENT_DDS_SWITCH_RESPONSE: { |
| log("EVENT_DDS_SWITCH_RESPONSE"); |
| onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj); |
| break; |
| } |
| case EVENT_PREFERRED_SUB_VALID: { |
| log("EVENT_PREFERRED_SUB_VALID"); |
| notifyDdsSwitchDone(); |
| break; |
| } |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| |
| private boolean isSimReady(int phoneId) { |
| if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) { |
| return false; |
| } |
| |
| if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(mSimStates[phoneId]) || |
| IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(mSimStates[phoneId]) || |
| IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(mSimStates[phoneId])) { |
| log("SIM READY for phoneId: " + phoneId); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @Override |
| protected boolean onEvaluate(boolean requestsChanged, String reason) { |
| StringBuilder sb = new StringBuilder(reason); |
| |
| boolean diffDetected = requestsChanged; |
| |
| // Check if user setting of default non-opportunistic data sub is changed. |
| final int primaryDataSubId = mSubscriptionController.getDefaultDataSubId(); |
| final int ddsPhoneId = mSubscriptionController.getPhoneId(primaryDataSubId); |
| if (primaryDataSubId != mPrimaryDataSubId) { |
| sb.append(" mPrimaryDataSubId ").append(mPrimaryDataSubId).append("->") |
| .append(primaryDataSubId); |
| mManualDdsSwitch = true; |
| mPrimaryDataSubId = primaryDataSubId; |
| } |
| |
| // Check to see if there is any active subscription on any phone |
| boolean hasAnyActiveSubscription = false; |
| boolean hasSubRefreshedOnThePreferredPhone = false; |
| |
| // Check if phoneId to subId mapping is changed. |
| for (int i = 0; i < mActiveModemCount; i++) { |
| int sub = mSubscriptionController.getSubIdUsingPhoneId(i); |
| |
| if (SubscriptionManager.isValidSubscriptionId(sub)) hasAnyActiveSubscription = true; |
| |
| if (sub != mPhoneSubscriptions[i]) { |
| sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]); |
| sb.append("->").append(sub); |
| if (SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get()) |
| && mPhoneSubscriptions[i] == mPreferredDataSubId.get()) { |
| sb.append("sub refreshed"); |
| hasSubRefreshedOnThePreferredPhone = true; |
| } |
| mPhoneSubscriptions[i] = sub; |
| diffDetected = true; |
| } |
| } |
| |
| if (!hasAnyActiveSubscription) { |
| transitionToEmergencyPhone(); |
| } else { |
| if (VDBG) log("Found an active subscription"); |
| } |
| final boolean isOldPeferredDataSubValid = |
| SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get()); |
| // Check if phoneId for preferred data is changed. |
| int oldPreferredDataPhoneId = mPreferredDataPhoneId; |
| |
| // When there are no subscriptions, the preferred data phone ID is invalid, but we want |
| // to keep a valid phoneId for Emergency, so skip logic that updates for preferred data |
| // phone ID. Ideally there should be a single set of checks that evaluate the correct |
| // phoneId on a service-by-service basis (EIMS being one), but for now... just bypass |
| // this logic in the no-SIM case. |
| if (hasAnyActiveSubscription) updatePreferredDataPhoneId(); |
| |
| final boolean isPeferredDataSubValid = |
| SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get()); |
| |
| if(!isOldPeferredDataSubValid && isPeferredDataSubValid) { |
| // To avoid race condition, I'd like to send a msg in OnEvalute |
| // This is used to ensure informing active phones again after the preferred |
| // SUB is valid |
| sendEmptyMessage(EVENT_PREFERRED_SUB_VALID); |
| } |
| |
| if (oldPreferredDataPhoneId != mPreferredDataPhoneId) { |
| sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId) |
| .append("->").append(mPreferredDataPhoneId); |
| if (SubscriptionManager.isValidPhoneId(oldPreferredDataPhoneId)) { |
| mDdsRequestSent[oldPreferredDataPhoneId] = false; |
| } |
| mDdsSwitchState = DdsSwitchState.REQUIRED; |
| diffDetected = true; |
| } else if (hasSubRefreshedOnThePreferredPhone) { |
| // Tell connectivity the real active data phone |
| notifyPreferredDataSubIdChanged(); |
| } |
| |
| if (diffDetected) { |
| log("evaluating due to " + sb.toString()); |
| if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) { |
| // With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach. |
| // So marking all phone as active. |
| for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { |
| activate(phoneId); |
| } |
| sendRilCommands(mPreferredDataPhoneId); |
| } else { |
| List<Integer> newActivePhones = new ArrayList<Integer>(); |
| |
| for (DcRequest dcRequest : mPrioritizedDcRequests) { |
| int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest, |
| dcRequest.apnType); |
| if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; |
| if (newActivePhones.contains(phoneIdForRequest)) continue; |
| newActivePhones.add(phoneIdForRequest); |
| if (newActivePhones.size() >= mMaxDataAttachModemCount) break; |
| } |
| |
| if (VDBG) { |
| log("default subId = " + mPrimaryDataSubId); |
| log("preferred subId = " + mPreferredDataSubId.get()); |
| for (int i = 0; i < mActiveModemCount; i++) { |
| log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]"); |
| } |
| log(" newActivePhones:"); |
| for (Integer i : newActivePhones) log(" " + i); |
| } |
| |
| mNewActivePhones = newActivePhones; |
| for (int phoneId = 0; (phoneId < mActiveModemCount); phoneId++) { |
| if (!newActivePhones.contains(phoneId)) { |
| deactivate(phoneId); |
| } |
| } |
| if (!mWaitForDetachResponse) { |
| // only activate phones up to the limit |
| final boolean activateDdsPhone = mNewActivePhones.contains(ddsPhoneId); |
| if (activateDdsPhone && mManualDdsSwitch) { |
| activate(ddsPhoneId); |
| } else { |
| for (int phoneId : newActivePhones) { |
| activate(phoneId); |
| } |
| } |
| if (activateDdsPhone) { |
| mManualDdsSwitch = false; |
| } |
| } |
| } |
| } |
| |
| return diffDetected; |
| } |
| |
| /* Determine the phone id on which PS attach needs to be done |
| */ |
| protected int phoneIdForRequest(NetworkRequest netRequest, int apnType) { |
| int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities |
| .getNetworkSpecifier()); |
| |
| if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId; |
| if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX; |
| |
| int preferredDataSubId = SubscriptionManager.isValidPhoneId(mPreferredDataPhoneId) |
| ? mPhoneSubscriptions[mPreferredDataPhoneId] : INVALID_SUBSCRIPTION_ID; |
| // Currently we assume multi-SIM devices will only support one Internet PDN connection. So |
| // if Internet PDN is established on the non-preferred phone, it will interrupt |
| // Internet connection on the preferred phone. So we only accept Internet request with |
| // preferred data subscription or no specified subscription. |
| if (netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) |
| && subId != preferredDataSubId && subId != mValidator.getSubIdInValidation()) { |
| // Returning INVALID_PHONE_INDEX will result in netRequest not being handled. |
| return INVALID_PHONE_INDEX; |
| } |
| |
| // This is for Volte+PS case |
| if ((ApnSetting.TYPE_IMS == apnType) && mManualDdsSwitch |
| && mMaxDataAttachModemCount != mActiveModemCount) { |
| subId = mPrimaryDataSubId; |
| } |
| |
| // Try to find matching phone ID. If it doesn't exist, we'll end up returning INVALID. |
| int phoneId = INVALID_PHONE_INDEX; |
| for (int i = 0; i < mActiveModemCount; i++) { |
| if (mPhoneSubscriptions[i] == subId) { |
| phoneId = i; |
| break; |
| } |
| } |
| return phoneId; |
| } |
| |
| protected boolean isUiccProvisioned(int phoneId) { |
| boolean isUiccApplicationEnabled = true; |
| // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it here |
| log("isUiccProvisioned: status= " + isUiccApplicationEnabled + " phoneid=" + phoneId); |
| return mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId]) && isUiccApplicationEnabled; |
| } |
| |
| @Override |
| protected void deactivate(int phoneId) { |
| PhoneState state = mPhoneStates[phoneId]; |
| if (state.active == false) { |
| return; |
| } |
| state.active = false; |
| log("deactivate " + phoneId); |
| state.lastRequested = System.currentTimeMillis(); |
| if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| if (mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId])) { |
| PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(false, |
| obtainMessage(EVENT_ALLOW_DATA_FALSE_RESPONSE)); |
| mWaitForDetachResponse = true; |
| } |
| } |
| } |
| |
| @Override |
| protected void activate(int phoneId) { |
| PhoneState state = mPhoneStates[phoneId]; |
| if ((state.active == true) && !mManualDdsSwitch && |
| (getConnectFailureCount(phoneId) == 0)) return; |
| state.active = true; |
| log("activate " + phoneId); |
| state.lastRequested = System.currentTimeMillis(); |
| if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(true, |
| obtainMessage(EVENT_ALLOW_DATA_TRUE_RESPONSE, phoneId, 0)); |
| } |
| } |
| |
| @Override |
| protected void sendRilCommands(int phoneId) { |
| if (!SubscriptionManager.isValidPhoneId(phoneId) || phoneId >= mActiveModemCount) { |
| log("sendRilCommands: skip dds switch due to invalid phoneid=" + phoneId); |
| return; |
| } |
| |
| if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { |
| PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(isPhoneActive(phoneId), |
| obtainMessage(isPhoneActive(phoneId) ? EVENT_ALLOW_DATA_TRUE_RESPONSE |
| : EVENT_ALLOW_DATA_FALSE_RESPONSE, phoneId, 0)); |
| } else if (phoneId == mPreferredDataPhoneId) { |
| if (!mDdsRequestSent[phoneId]) { |
| // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId |
| log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId); |
| mRadioConfig.setPreferredDataModem(phoneId, |
| obtainMessage(EVENT_DDS_SWITCH_RESPONSE, phoneId, 0)); |
| mDdsRequestSent[phoneId] = true; |
| } else { |
| log("sendRilCommands: setPreferredDataModem request already sent on phoneId: " + |
| phoneId); |
| } |
| } |
| } |
| |
| /* |
| * Method to check if any of the calls are started |
| */ |
| @Override |
| protected boolean isPhoneInVoiceCall(Phone phone) { |
| if (phone == null) { |
| return false; |
| } |
| boolean dataDuringCallsEnabled = false; |
| DataEnabledSettings dataEnabledSettings = phone.getDataEnabledSettings(); |
| if (dataEnabledSettings != null) { |
| dataDuringCallsEnabled = dataEnabledSettings.isDataAllowedInVoiceCall(); |
| } |
| if (!dataDuringCallsEnabled) { |
| log("isPhoneInVoiceCall: dataDuringCallsEnabled=" + dataDuringCallsEnabled); |
| return false; |
| } |
| int phoneId = phone.getPhoneId(); |
| return (mFgCsCalls[phoneId].getState().isAlive() || |
| mBgCsCalls[phoneId].getState().isAlive() || |
| mRiCsCalls[phoneId].getState().isAlive() || |
| mFgImsCalls[phoneId].getState().isAlive() || |
| mBgImsCalls[phoneId].getState().isAlive() || |
| mRiImsCalls[phoneId].getState().isAlive()); |
| } |
| |
| private void resetConnectFailureCount(int phoneId) { |
| mAllowDataFailure[phoneId] = 0; |
| } |
| |
| private void incConnectFailureCount(int phoneId) { |
| mAllowDataFailure[phoneId]++; |
| } |
| |
| @VisibleForTesting |
| public int getConnectFailureCount(int phoneId) { |
| return mAllowDataFailure[phoneId]; |
| } |
| |
| private void handleConnectMaxFailure(int phoneId) { |
| resetConnectFailureCount(phoneId); |
| int ddsSubId = mSubscriptionController.getDefaultDataSubId(); |
| int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId); |
| if (SubscriptionManager.isValidPhoneId(ddsPhoneId) && phoneId != ddsPhoneId) { |
| log("ALLOW_DATA retries exhausted on phoneId = " + phoneId); |
| enforceDds(ddsPhoneId); |
| } |
| } |
| |
| private void enforceDds(int phoneId) { |
| int[] subId = mSubscriptionController.getSubId(phoneId); |
| log("enforceDds: subId = " + subId[0]); |
| mSubscriptionController.setDefaultDataSubId(subId[0]); |
| } |
| |
| private boolean isAnyVoiceCallActiveOnDevice() { |
| boolean ret = (CallManager.getInstance().getState() != PhoneConstants.State.IDLE); |
| log("isAnyVoiceCallActiveOnDevice: " + ret); |
| return ret; |
| } |
| |
| private void onDdsSwitchResponse(int phoneId, AsyncResult ar) { |
| if (ar.exception != null) { |
| mDdsRequestSent[phoneId] = false; |
| incConnectFailureCount(phoneId); |
| log("Dds switch failed on phoneId = " + phoneId + ", failureCount = " |
| + getConnectFailureCount(phoneId)); |
| |
| if (isAnyVoiceCallActiveOnDevice()) { |
| boolean isTempSwitchPropEnabled = SystemProperties.getBoolean( |
| PROPERTY_TEMP_DDSSWITCH, false); |
| int ddsPhoneId = mSubscriptionController.getPhoneId( |
| mSubscriptionController.getDefaultDataSubId()); |
| log("onDdsSwitchResponse: isTempSwitchPropEnabled=" + isTempSwitchPropEnabled + |
| ", ddsPhoneId=" + ddsPhoneId + ", mPreferredDataPhoneId=" + |
| mPreferredDataPhoneId); |
| if (isTempSwitchPropEnabled && (phoneId != ddsPhoneId) && |
| getConnectFailureCount(phoneId) < MAX_CONNECT_FAILURE_COUNT) { |
| log("Retry Temporary DDS switch on phoneId:" + phoneId); |
| sendRilCommands(phoneId); |
| } else { |
| /* Any DDS retry while voice call is active is in vain |
| Wait for call to get disconnected */ |
| log("Wait for call end indication"); |
| } |
| return; |
| } |
| |
| if (!isSimReady(phoneId)) { |
| /* If there is a attach failure due to sim not ready then |
| hold the retry until sim gets ready */ |
| log("Wait for SIM to get READY"); |
| return; |
| } |
| |
| int ddsSwitchFailureCount = getConnectFailureCount(phoneId); |
| if (ddsSwitchFailureCount > MAX_CONNECT_FAILURE_COUNT) { |
| handleConnectMaxFailure(phoneId); |
| } else { |
| int retryDelay = mRetryArray[ddsSwitchFailureCount - 1] * 1000; |
| log("Scheduling DDS switch retry after: " + retryDelay); |
| postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| log("Running DDS switch retry"); |
| if (isPhoneIdValidForRetry(phoneId)) { |
| sendRilCommands(phoneId); |
| } else { |
| log("Abandon DDS switch retry"); |
| resetConnectFailureCount(phoneId); |
| } |
| }}, retryDelay); |
| } |
| } else { |
| log("DDS switch success on phoneId = " + phoneId); |
| resetConnectFailureCount(phoneId); |
| if (mDdsSwitchState == DdsSwitchState.REQUIRED) { |
| mDdsSwitchState = DdsSwitchState.DONE; |
| } |
| notifyDdsSwitchDone(); |
| } |
| } |
| |
| private void notifyDdsSwitchDone() { |
| log("notifyDdsSwitchDone on the preferred data SUB = " + mPreferredDataSubId.get() |
| + " and the preferred phone ID = " + mPreferredDataPhoneId); |
| // Notify all registrants. |
| mActivePhoneRegistrants.notifyRegistrants(); |
| notifyPreferredDataSubIdChanged(); |
| |
| if (mDdsSwitchState == DdsSwitchState.DONE |
| && SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get())) { |
| mDdsSwitchState = mDdsSwitchState.NONE; |
| Intent intent = new Intent( |
| "org.codeaurora.intent.action.ACTION_DDS_SWITCH_DONE"); |
| intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPreferredDataSubId.get()); |
| intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); |
| log("Broadcast dds switch done intent on " + mPreferredDataSubId.get()); |
| mContext.sendBroadcast(intent); |
| } |
| } |
| |
| private boolean isPhoneIdValidForRetry(int phoneId) { |
| boolean isValid = false; |
| int phoneIdForRequest = INVALID_PHONE_INDEX; |
| int ddsPhoneId = mSubscriptionController.getPhoneId( |
| mSubscriptionController.getDefaultDataSubId()); |
| if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) { |
| isValid = true; |
| } else { |
| if (mPrioritizedDcRequests.size() > 0) { |
| for (int i = 0; i < mMaxDataAttachModemCount; i++) { |
| DcRequest dcRequest = mPrioritizedDcRequests.get(i); |
| if (dcRequest != null) { |
| phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest, |
| dcRequest.apnType); |
| if (phoneIdForRequest == phoneId) { |
| isValid = true; |
| break; |
| } |
| } |
| } |
| } |
| } |
| return isValid; |
| } |
| |
| /* |
| * Returns true if mPhoneIdInVoiceCall is set for active calls |
| */ |
| private boolean isCallInProgress() { |
| return SubscriptionManager.isValidPhoneId(mPhoneIdInVoiceCall); |
| } |
| } |