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