1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.vendor; 18 19 import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 20 import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX; 21 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 22 import static android.telephony.TelephonyManager.RADIO_POWER_UNAVAILABLE; 23 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.net.NetworkCapabilities; 29 import android.net.NetworkRequest; 30 import android.os.AsyncResult; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.Registrant; 34 import android.os.SystemProperties; 35 import android.telephony.data.ApnSetting; 36 import android.telephony.Rlog; 37 import android.telephony.SubscriptionManager; 38 39 import android.text.TextUtils; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.telephony.CallManager; 43 import com.android.internal.telephony.Call; 44 import com.android.internal.telephony.CommandsInterface; 45 import com.android.internal.telephony.dataconnection.DcRequest; 46 import com.android.internal.telephony.dataconnection.DataEnabledSettings; 47 import com.android.internal.telephony.GsmCdmaCall; 48 import com.android.internal.telephony.imsphone.ImsPhone; 49 import com.android.internal.telephony.imsphone.ImsPhoneCall; 50 import com.android.internal.telephony.ITelephonyRegistry; 51 import com.android.internal.telephony.IccCardConstants; 52 import com.android.internal.telephony.Phone; 53 import com.android.internal.telephony.PhoneConstants; 54 import com.android.internal.telephony.PhoneFactory; 55 import com.android.internal.telephony.PhoneSwitcher; 56 import com.android.internal.telephony.SubscriptionController; 57 import com.android.internal.telephony.TelephonyIntents; 58 59 import java.lang.Integer; 60 import java.nio.ByteBuffer; 61 import java.nio.ByteOrder; 62 import java.util.ArrayList; 63 import java.util.List; 64 import android.net.StringNetworkSpecifier; 65 import android.net.NetworkSpecifier; 66 67 public class VendorPhoneSwitcher extends PhoneSwitcher { 68 69 private final int MAX_CONNECT_FAILURE_COUNT = 5; 70 private final int[] mRetryArray = new int []{5,10,20,40,60}; 71 private int[] mAllowDataFailure; 72 private boolean[] mDdsRequestSent; 73 private boolean mManualDdsSwitch = false; 74 private int mDefaultDataPhoneId = -1; 75 private String [] mSimStates; 76 private List<Integer> mNewActivePhones; 77 private boolean mWaitForDetachResponse = false; 78 private DdsSwitchState mDdsSwitchState = DdsSwitchState.NONE; 79 private final int USER_INITIATED_SWITCH = 0; 80 private final int NONUSER_INITIATED_SWITCH = 1; 81 private final String PROPERTY_TEMP_DDSSWITCH = "persist.vendor.radio.enable_temp_dds"; 82 private final GsmCdmaCall[] mFgCsCalls; 83 private final GsmCdmaCall[] mBgCsCalls; 84 private final GsmCdmaCall[] mRiCsCalls; 85 private final ImsPhone[] mImsPhones; 86 private final ImsPhoneCall[] mFgImsCalls; 87 private final ImsPhoneCall[] mBgImsCalls; 88 private final ImsPhoneCall[] mRiImsCalls; 89 90 private final int EVENT_ALLOW_DATA_FALSE_RESPONSE = 201; 91 private final int EVENT_ALLOW_DATA_TRUE_RESPONSE = 202; 92 private final int EVENT_DDS_SWITCH_RESPONSE = 203; 93 private final int EVENT_PREFERRED_SUB_VALID = 204; 94 95 private enum DdsSwitchState { 96 NONE, REQUIRED, DONE 97 } 98 VendorPhoneSwitcher(int maxActivePhones, Context context, Looper looper)99 public VendorPhoneSwitcher(int maxActivePhones, Context context, Looper looper) { 100 super (maxActivePhones, context, looper); 101 mAllowDataFailure = new int[mActiveModemCount]; 102 mDdsRequestSent = new boolean[mActiveModemCount]; 103 mSimStates = new String[mActiveModemCount]; 104 IntentFilter filter = new IntentFilter(); 105 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 106 mContext.registerReceiver(mSimStateIntentReceiver, filter); 107 108 mImsPhones = new ImsPhone[mActiveModemCount]; 109 mFgCsCalls = new GsmCdmaCall[mActiveModemCount]; 110 mBgCsCalls = new GsmCdmaCall[mActiveModemCount]; 111 mRiCsCalls = new GsmCdmaCall[mActiveModemCount]; 112 mFgImsCalls = new ImsPhoneCall[mActiveModemCount]; 113 mBgImsCalls = new ImsPhoneCall[mActiveModemCount]; 114 mRiImsCalls = new ImsPhoneCall[mActiveModemCount]; 115 116 for (int i=0; i < mActiveModemCount; i++) { 117 if (PhoneFactory.getPhone(i) != null) { 118 mFgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getForegroundCall(); 119 mBgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getBackgroundCall(); 120 mRiCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getRingingCall(); 121 } 122 mImsPhones[i] = (ImsPhone)PhoneFactory.getPhone(i).getImsPhone(); 123 if (mImsPhones[i] != null) { 124 mFgImsCalls[i] = mImsPhones[i].getForegroundCall(); 125 mBgImsCalls[i] = mImsPhones[i].getBackgroundCall(); 126 mRiImsCalls[i] = mImsPhones[i].getRingingCall(); 127 } 128 129 mDdsRequestSent[i] = false; 130 } 131 } 132 make(int maxActivePhones, Context context, Looper looper)133 public static VendorPhoneSwitcher make(int maxActivePhones, Context context, Looper looper) { 134 if (sPhoneSwitcher == null) { 135 sPhoneSwitcher = new VendorPhoneSwitcher(maxActivePhones, context, looper); 136 } 137 138 return (VendorPhoneSwitcher)sPhoneSwitcher; 139 } 140 141 private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() { 142 @Override 143 public void onReceive(Context context, Intent intent) { 144 String action = intent.getAction(); 145 if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 146 String value = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 147 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 148 SubscriptionManager.INVALID_PHONE_INDEX); 149 log("mSimStateIntentReceiver: phoneId = " + phoneId + " value = " + value); 150 if (SubscriptionManager.isValidPhoneId(phoneId)) { 151 mSimStates[phoneId] = value; 152 // If SIM is absent, allow DDS request always, which avoids DDS switch 153 // can't be completed in the no-SIM case because the sent status of the 154 // old preferred phone has no chance to reset in hot-swap 155 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(value)) { 156 mDdsRequestSent[phoneId] = false; 157 } 158 } 159 160 if (isSimReady(phoneId) && (getConnectFailureCount(phoneId) > 0)) { 161 sendRilCommands(phoneId); 162 } 163 } 164 } 165 }; 166 167 @Override handleMessage(Message msg)168 public void handleMessage(Message msg) { 169 final int ddsSubId = mSubscriptionController.getDefaultDataSubId(); 170 final int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId); 171 172 log("handle event - " + msg.what); 173 AsyncResult ar = null; 174 switch (msg.what) { 175 case EVENT_SUBSCRIPTION_CHANGED: { 176 if (mHalCommandToUse == HAL_COMMAND_UNKNOWN) { 177 log("EVENT_SUBSCRIPTION_CHANGED: update HAL command"); 178 mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported() 179 ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA; 180 } 181 onEvaluate(REQUESTS_UNCHANGED, "subChanged"); 182 break; 183 } 184 case EVENT_PRECISE_CALL_STATE_CHANGED: { 185 log("EVENT_PRECISE_CALL_STATE_CHANGED"); 186 if (!isAnyVoiceCallActiveOnDevice()) { 187 for (int i = 0; i < mActiveModemCount; i++) { 188 if ((getConnectFailureCount(i) > 0) && 189 isPhoneIdValidForRetry(i)) { 190 sendRilCommands(i); 191 break; 192 } 193 } 194 } 195 super.handleMessage(msg); 196 break; 197 } 198 case EVENT_ALLOW_DATA_TRUE_RESPONSE: { 199 log("EVENT_ALLOW_DATA_TRUE_RESPONSE"); 200 onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj); 201 break; 202 } 203 case EVENT_ALLOW_DATA_FALSE_RESPONSE: { 204 log("EVENT_ALLOW_DATA_FALSE_RESPONSE"); 205 mWaitForDetachResponse = false; 206 for (int phoneId : mNewActivePhones) { 207 activate(phoneId); 208 } 209 if (mNewActivePhones.contains(ddsPhoneId)) { 210 mManualDdsSwitch = false; 211 } 212 break; 213 } 214 case EVENT_DDS_SWITCH_RESPONSE: { 215 log("EVENT_DDS_SWITCH_RESPONSE"); 216 onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj); 217 break; 218 } 219 case EVENT_PREFERRED_SUB_VALID: { 220 log("EVENT_PREFERRED_SUB_VALID"); 221 notifyDdsSwitchDone(); 222 break; 223 } 224 default: 225 super.handleMessage(msg); 226 } 227 } 228 isSimReady(int phoneId)229 private boolean isSimReady(int phoneId) { 230 if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) { 231 return false; 232 } 233 234 if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(mSimStates[phoneId]) || 235 IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(mSimStates[phoneId]) || 236 IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(mSimStates[phoneId])) { 237 log("SIM READY for phoneId: " + phoneId); 238 return true; 239 } else { 240 return false; 241 } 242 } 243 244 @Override onEvaluate(boolean requestsChanged, String reason)245 protected boolean onEvaluate(boolean requestsChanged, String reason) { 246 StringBuilder sb = new StringBuilder(reason); 247 248 boolean diffDetected = requestsChanged; 249 250 // Check if user setting of default non-opportunistic data sub is changed. 251 final int primaryDataSubId = mSubscriptionController.getDefaultDataSubId(); 252 final int ddsPhoneId = mSubscriptionController.getPhoneId(primaryDataSubId); 253 if (primaryDataSubId != mPrimaryDataSubId) { 254 sb.append(" mPrimaryDataSubId ").append(mPrimaryDataSubId).append("->") 255 .append(primaryDataSubId); 256 mManualDdsSwitch = true; 257 mPrimaryDataSubId = primaryDataSubId; 258 } 259 260 // Check to see if there is any active subscription on any phone 261 boolean hasAnyActiveSubscription = false; 262 boolean hasSubRefreshedOnThePreferredPhone = false; 263 264 // Check if phoneId to subId mapping is changed. 265 for (int i = 0; i < mActiveModemCount; i++) { 266 int sub = mSubscriptionController.getSubIdUsingPhoneId(i); 267 268 if (SubscriptionManager.isValidSubscriptionId(sub)) hasAnyActiveSubscription = true; 269 270 if (sub != mPhoneSubscriptions[i]) { 271 sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]); 272 sb.append("->").append(sub); 273 if (SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId) 274 && mPhoneSubscriptions[i] == mPreferredDataSubId) { 275 sb.append("sub refreshed"); 276 hasSubRefreshedOnThePreferredPhone = true; 277 } 278 mPhoneSubscriptions[i] = sub; 279 diffDetected = true; 280 } 281 } 282 283 if (!hasAnyActiveSubscription) { 284 transitionToEmergencyPhone(); 285 } else { 286 if (VDBG) log("Found an active subscription"); 287 } 288 final boolean isOldPeferredDataSubValid = 289 SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId); 290 // Check if phoneId for preferred data is changed. 291 int oldPreferredDataPhoneId = mPreferredDataPhoneId; 292 293 // When there are no subscriptions, the preferred data phone ID is invalid, but we want 294 // to keep a valid phoneId for Emergency, so skip logic that updates for preferred data 295 // phone ID. Ideally there should be a single set of checks that evaluate the correct 296 // phoneId on a service-by-service basis (EIMS being one), but for now... just bypass 297 // this logic in the no-SIM case. 298 if (hasAnyActiveSubscription) updatePreferredDataPhoneId(); 299 300 final boolean isPeferredDataSubValid = 301 SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId); 302 303 if(!isOldPeferredDataSubValid && isPeferredDataSubValid) { 304 // To avoid race condition, I'd like to send a msg in OnEvalute 305 // This is used to ensure informing active phones again after the preferred 306 // SUB is valid 307 sendEmptyMessage(EVENT_PREFERRED_SUB_VALID); 308 } 309 310 if (oldPreferredDataPhoneId != mPreferredDataPhoneId) { 311 sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId) 312 .append("->").append(mPreferredDataPhoneId); 313 if (SubscriptionManager.isValidPhoneId(oldPreferredDataPhoneId)) { 314 mDdsRequestSent[oldPreferredDataPhoneId] = false; 315 } 316 mDdsSwitchState = DdsSwitchState.REQUIRED; 317 diffDetected = true; 318 } else if (hasSubRefreshedOnThePreferredPhone) { 319 // Tell connectivity the real active data phone 320 notifyPreferredDataSubIdChanged(); 321 } 322 323 if (diffDetected) { 324 log("evaluating due to " + sb.toString()); 325 if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) { 326 // With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach. 327 // So marking all phone as active. 328 for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { 329 activate(phoneId); 330 } 331 sendRilCommands(mPreferredDataPhoneId); 332 } else { 333 List<Integer> newActivePhones = new ArrayList<Integer>(); 334 335 for (DcRequest dcRequest : mPrioritizedDcRequests) { 336 int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest, 337 dcRequest.apnType); 338 if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; 339 if (newActivePhones.contains(phoneIdForRequest)) continue; 340 newActivePhones.add(phoneIdForRequest); 341 if (newActivePhones.size() >= mMaxDataAttachModemCount) break; 342 } 343 344 if (VDBG) { 345 log("default subId = " + mPrimaryDataSubId); 346 log("preferred subId = " + mPreferredDataSubId); 347 for (int i = 0; i < mActiveModemCount; i++) { 348 log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]"); 349 } 350 log(" newActivePhones:"); 351 for (Integer i : newActivePhones) log(" " + i); 352 } 353 354 mNewActivePhones = newActivePhones; 355 for (int phoneId = 0; (phoneId < mActiveModemCount); phoneId++) { 356 if (!newActivePhones.contains(phoneId)) { 357 deactivate(phoneId); 358 } 359 } 360 if (!mWaitForDetachResponse) { 361 // only activate phones up to the limit 362 final boolean activateDdsPhone = mNewActivePhones.contains(ddsPhoneId); 363 if (activateDdsPhone && mManualDdsSwitch) { 364 activate(ddsPhoneId); 365 } else { 366 for (int phoneId : newActivePhones) { 367 activate(phoneId); 368 } 369 } 370 if (activateDdsPhone) { 371 mManualDdsSwitch = false; 372 } 373 } 374 } 375 } 376 377 return diffDetected; 378 } 379 380 /* Determine the phone id on which PS attach needs to be done 381 */ phoneIdForRequest(NetworkRequest netRequest, int apnType)382 protected int phoneIdForRequest(NetworkRequest netRequest, int apnType) { 383 int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities 384 .getNetworkSpecifier()); 385 386 if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId; 387 if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX; 388 389 int preferredDataSubId = SubscriptionManager.isValidPhoneId(mPreferredDataPhoneId) 390 ? mPhoneSubscriptions[mPreferredDataPhoneId] : INVALID_SUBSCRIPTION_ID; 391 // Currently we assume multi-SIM devices will only support one Internet PDN connection. So 392 // if Internet PDN is established on the non-preferred phone, it will interrupt 393 // Internet connection on the preferred phone. So we only accept Internet request with 394 // preferred data subscription or no specified subscription. 395 if (netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 396 && netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 397 && subId != preferredDataSubId && subId != mValidator.getSubIdInValidation()) { 398 // Returning INVALID_PHONE_INDEX will result in netRequest not being handled. 399 return INVALID_PHONE_INDEX; 400 } 401 402 // This is for Volte+PS case 403 if ((ApnSetting.TYPE_IMS == apnType) && mManualDdsSwitch 404 && mMaxDataAttachModemCount != mActiveModemCount) { 405 subId = mPrimaryDataSubId; 406 } 407 408 // Try to find matching phone ID. If it doesn't exist, we'll end up returning INVALID. 409 int phoneId = INVALID_PHONE_INDEX; 410 for (int i = 0; i < mActiveModemCount; i++) { 411 if (mPhoneSubscriptions[i] == subId) { 412 phoneId = i; 413 break; 414 } 415 } 416 return phoneId; 417 } 418 isUiccProvisioned(int phoneId)419 protected boolean isUiccProvisioned(int phoneId) { 420 boolean isUiccApplicationEnabled = true; 421 // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it here 422 log("isUiccProvisioned: status= " + isUiccApplicationEnabled + " phoneid=" + phoneId); 423 return mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId]) && isUiccApplicationEnabled; 424 } 425 426 @Override deactivate(int phoneId)427 protected void deactivate(int phoneId) { 428 PhoneState state = mPhoneStates[phoneId]; 429 if (state.active == false) { 430 return; 431 } 432 state.active = false; 433 log("deactivate " + phoneId); 434 state.lastRequested = System.currentTimeMillis(); 435 if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { 436 if (mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId])) { 437 PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(false, 438 obtainMessage(EVENT_ALLOW_DATA_FALSE_RESPONSE)); 439 mWaitForDetachResponse = true; 440 } 441 } 442 } 443 444 @Override activate(int phoneId)445 protected void activate(int phoneId) { 446 PhoneState state = mPhoneStates[phoneId]; 447 if ((state.active == true) && !mManualDdsSwitch && 448 (getConnectFailureCount(phoneId) == 0)) return; 449 state.active = true; 450 log("activate " + phoneId); 451 state.lastRequested = System.currentTimeMillis(); 452 if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { 453 PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(true, 454 obtainMessage(EVENT_ALLOW_DATA_TRUE_RESPONSE, phoneId, 0)); 455 } 456 } 457 458 @Override sendRilCommands(int phoneId)459 protected void sendRilCommands(int phoneId) { 460 if (!SubscriptionManager.isValidPhoneId(phoneId) || phoneId >= mActiveModemCount) { 461 log("sendRilCommands: skip dds switch due to invalid phoneid=" + phoneId); 462 return; 463 } 464 465 if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) { 466 PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(isPhoneActive(phoneId), 467 obtainMessage(isPhoneActive(phoneId) ? EVENT_ALLOW_DATA_TRUE_RESPONSE 468 : EVENT_ALLOW_DATA_FALSE_RESPONSE, phoneId, 0)); 469 } else if (phoneId == mPreferredDataPhoneId) { 470 if (!mDdsRequestSent[phoneId]) { 471 // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId 472 log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId); 473 mRadioConfig.setPreferredDataModem(phoneId, 474 obtainMessage(EVENT_DDS_SWITCH_RESPONSE, phoneId, 0)); 475 mDdsRequestSent[phoneId] = true; 476 } else { 477 log("sendRilCommands: setPreferredDataModem request already sent on phoneId: " + 478 phoneId); 479 } 480 } 481 } 482 483 /* 484 * Method to check if any of the calls are started 485 */ 486 @Override isPhoneInVoiceCall(Phone phone)487 protected boolean isPhoneInVoiceCall(Phone phone) { 488 if (phone == null) { 489 return false; 490 } 491 boolean dataDuringCallsEnabled = false; 492 DataEnabledSettings dataEnabledSettings = phone.getDataEnabledSettings(); 493 if (dataEnabledSettings != null) { 494 dataDuringCallsEnabled = dataEnabledSettings.isDataAllowedInVoiceCall(); 495 } 496 if (!dataDuringCallsEnabled) { 497 log("isPhoneInVoiceCall: dataDuringCallsEnabled=" + dataDuringCallsEnabled); 498 return false; 499 } 500 int phoneId = phone.getPhoneId(); 501 return (mFgCsCalls[phoneId].getState().isAlive() || 502 mBgCsCalls[phoneId].getState().isAlive() || 503 mRiCsCalls[phoneId].getState().isAlive() || 504 mFgImsCalls[phoneId].getState().isAlive() || 505 mBgImsCalls[phoneId].getState().isAlive() || 506 mRiImsCalls[phoneId].getState().isAlive()); 507 } 508 resetConnectFailureCount(int phoneId)509 private void resetConnectFailureCount(int phoneId) { 510 mAllowDataFailure[phoneId] = 0; 511 } 512 incConnectFailureCount(int phoneId)513 private void incConnectFailureCount(int phoneId) { 514 mAllowDataFailure[phoneId]++; 515 } 516 517 @VisibleForTesting getConnectFailureCount(int phoneId)518 public int getConnectFailureCount(int phoneId) { 519 return mAllowDataFailure[phoneId]; 520 } 521 handleConnectMaxFailure(int phoneId)522 private void handleConnectMaxFailure(int phoneId) { 523 resetConnectFailureCount(phoneId); 524 int ddsSubId = mSubscriptionController.getDefaultDataSubId(); 525 int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId); 526 if (SubscriptionManager.isValidPhoneId(ddsPhoneId) && phoneId != ddsPhoneId) { 527 log("ALLOW_DATA retries exhausted on phoneId = " + phoneId); 528 enforceDds(ddsPhoneId); 529 } 530 } 531 enforceDds(int phoneId)532 private void enforceDds(int phoneId) { 533 int[] subId = mSubscriptionController.getSubId(phoneId); 534 log("enforceDds: subId = " + subId[0]); 535 mSubscriptionController.setDefaultDataSubId(subId[0]); 536 } 537 isAnyVoiceCallActiveOnDevice()538 private boolean isAnyVoiceCallActiveOnDevice() { 539 boolean ret = (CallManager.getInstance().getState() != PhoneConstants.State.IDLE); 540 log("isAnyVoiceCallActiveOnDevice: " + ret); 541 return ret; 542 } 543 onDdsSwitchResponse(int phoneId, AsyncResult ar)544 private void onDdsSwitchResponse(int phoneId, AsyncResult ar) { 545 if (ar.exception != null) { 546 mDdsRequestSent[phoneId] = false; 547 incConnectFailureCount(phoneId); 548 log("Dds switch failed on phoneId = " + phoneId + ", failureCount = " 549 + getConnectFailureCount(phoneId)); 550 551 if (isAnyVoiceCallActiveOnDevice()) { 552 boolean isTempSwitchPropEnabled = SystemProperties.getBoolean( 553 PROPERTY_TEMP_DDSSWITCH, false); 554 int ddsPhoneId = mSubscriptionController.getPhoneId( 555 mSubscriptionController.getDefaultDataSubId()); 556 log("onDdsSwitchResponse: isTempSwitchPropEnabled=" + isTempSwitchPropEnabled + 557 ", ddsPhoneId=" + ddsPhoneId + ", mPreferredDataPhoneId=" + 558 mPreferredDataPhoneId); 559 if (isTempSwitchPropEnabled && (phoneId != ddsPhoneId) && 560 getConnectFailureCount(phoneId) < MAX_CONNECT_FAILURE_COUNT) { 561 log("Retry Temporary DDS switch on phoneId:" + phoneId); 562 sendRilCommands(phoneId); 563 } else { 564 /* Any DDS retry while voice call is active is in vain 565 Wait for call to get disconnected */ 566 log("Wait for call end indication"); 567 } 568 return; 569 } 570 571 if (!isSimReady(phoneId)) { 572 /* If there is a attach failure due to sim not ready then 573 hold the retry until sim gets ready */ 574 log("Wait for SIM to get READY"); 575 return; 576 } 577 578 int ddsSwitchFailureCount = getConnectFailureCount(phoneId); 579 if (ddsSwitchFailureCount > MAX_CONNECT_FAILURE_COUNT) { 580 handleConnectMaxFailure(phoneId); 581 } else { 582 int retryDelay = mRetryArray[ddsSwitchFailureCount - 1] * 1000; 583 log("Scheduling DDS switch retry after: " + retryDelay); 584 postDelayed(new Runnable() { 585 @Override 586 public void run() { 587 log("Running DDS switch retry"); 588 if (isPhoneIdValidForRetry(phoneId)) { 589 sendRilCommands(phoneId); 590 } else { 591 log("Abandon DDS switch retry"); 592 resetConnectFailureCount(phoneId); 593 } 594 }}, retryDelay); 595 } 596 } else { 597 log("DDS switch success on phoneId = " + phoneId); 598 resetConnectFailureCount(phoneId); 599 if (mDdsSwitchState == DdsSwitchState.REQUIRED) { 600 mDdsSwitchState = DdsSwitchState.DONE; 601 } 602 notifyDdsSwitchDone(); 603 } 604 } 605 notifyDdsSwitchDone()606 private void notifyDdsSwitchDone() { 607 log("notifyDdsSwitchDone on the preferred data SUB = " + mPreferredDataSubId 608 + " and the preferred phone ID = " + mPreferredDataPhoneId); 609 // Notify all registrants. 610 mActivePhoneRegistrants.notifyRegistrants(); 611 notifyPreferredDataSubIdChanged(); 612 613 if (mDdsSwitchState == DdsSwitchState.DONE 614 && SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId)) { 615 mDdsSwitchState = mDdsSwitchState.NONE; 616 Intent intent = new Intent( 617 "org.codeaurora.intent.action.ACTION_DDS_SWITCH_DONE"); 618 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPreferredDataSubId); 619 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 620 log("Broadcast dds switch done intent on " + mPreferredDataSubId); 621 mContext.sendBroadcast(intent); 622 } 623 } 624 isPhoneIdValidForRetry(int phoneId)625 private boolean isPhoneIdValidForRetry(int phoneId) { 626 boolean isValid = false; 627 int phoneIdForRequest = INVALID_PHONE_INDEX; 628 int ddsPhoneId = mSubscriptionController.getPhoneId( 629 mSubscriptionController.getDefaultDataSubId()); 630 if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) { 631 isValid = true; 632 } else { 633 if (mPrioritizedDcRequests.size() > 0) { 634 for (int i = 0; i < mMaxDataAttachModemCount; i++) { 635 DcRequest dcRequest = mPrioritizedDcRequests.get(i); 636 if (dcRequest != null) { 637 phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest, 638 dcRequest.apnType); 639 if (phoneIdForRequest == phoneId) { 640 isValid = true; 641 break; 642 } 643 } 644 } 645 } 646 } 647 return isValid; 648 } 649 650 /* 651 * Returns true if mPhoneIdInVoiceCall is set for active calls 652 */ isCallInProgress()653 private boolean isCallInProgress() { 654 return SubscriptionManager.isValidPhoneId(mPhoneIdInVoiceCall); 655 } 656 } 657