blob: 6949adea209e6a5c7f64f33600d559c9d70d1bfe [file] [log] [blame]
/*
* 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);
}
}