1 /* 2 * Copyright (C) 2018 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.ons; 18 19 import static android.telephony.AvailableNetworkInfo.PRIORITY_HIGH; 20 import static android.telephony.AvailableNetworkInfo.PRIORITY_LOW; 21 22 import android.app.PendingIntent; 23 import android.compat.Compatibility; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.AsyncTask; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.Message; 30 import android.os.RemoteException; 31 import android.telephony.AvailableNetworkInfo; 32 import android.telephony.CellInfo; 33 import android.telephony.CellInfoLte; 34 import android.telephony.CellInfoNr; 35 import android.telephony.SignalStrength; 36 import android.telephony.SubscriptionInfo; 37 import android.telephony.SubscriptionManager; 38 import android.telephony.TelephonyManager; 39 import android.telephony.UiccCardInfo; 40 import android.telephony.UiccPortInfo; 41 import android.telephony.euicc.EuiccManager; 42 import android.text.TextUtils; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.telephony.ISetOpportunisticDataCallback; 46 import com.android.internal.telephony.IUpdateAvailableNetworksCallback; 47 import com.android.telephony.Rlog; 48 49 import java.util.ArrayList; 50 import java.util.Collections; 51 import java.util.Comparator; 52 import java.util.HashMap; 53 import java.util.HashSet; 54 import java.util.List; 55 import java.util.stream.Collectors; 56 57 /** 58 * Profile selector class which will select the right profile based upon 59 * geographic information input and network scan results. 60 */ 61 public class ONSProfileSelector { 62 private static final String LOG_TAG = "ONSProfileSelector"; 63 private static final boolean DBG = true; 64 private final Object mLock = new Object(); 65 66 private static final int INVALID_SEQUENCE_ID = -1; 67 private static final int START_SEQUENCE_ID = 1; 68 69 /* message to indicate profile update */ 70 private static final int MSG_PROFILE_UPDATE = 1; 71 72 /* message to indicate start of profile selection process */ 73 private static final int MSG_START_PROFILE_SELECTION = 2; 74 75 /* message to indicate Subscription switch completion */ 76 private static final int MSG_SUB_SWITCH_COMPLETE = 3; 77 78 /* message to stop profile selection process */ 79 private static final int MSG_STOP_PROFILE_SELECTION = 4; 80 81 private boolean mIsEnabled = false; 82 83 @VisibleForTesting 84 protected Context mContext; 85 86 @VisibleForTesting 87 protected TelephonyManager mTelephonyManager; 88 @VisibleForTesting 89 protected TelephonyManager mSubscriptionBoundTelephonyManager; 90 @VisibleForTesting 91 protected EuiccManager mEuiccManager; 92 93 @VisibleForTesting 94 protected ONSNetworkScanCtlr mNetworkScanCtlr; 95 96 @VisibleForTesting 97 protected SubscriptionManager mSubscriptionManager; 98 @VisibleForTesting 99 protected List<SubscriptionInfo> mOppSubscriptionInfos; 100 @VisibleForTesting 101 protected List<SubscriptionInfo> mStandaloneOppSubInfos; 102 private ONSProfileSelectionCallback mProfileSelectionCallback; 103 private int mSequenceId; 104 private int mSubId; 105 @VisibleForTesting 106 protected int mCurrentDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 107 private ArrayList<AvailableNetworkInfo> mAvailableNetworkInfos; 108 private IUpdateAvailableNetworksCallback mNetworkScanCallback; 109 110 public static final String ACTION_SUB_SWITCH = 111 "android.intent.action.SUBSCRIPTION_SWITCH_REPLY"; 112 113 HandlerThread mThread; 114 @VisibleForTesting 115 protected Handler mHandler; 116 117 /** 118 * Network scan callback handler 119 */ 120 @VisibleForTesting 121 protected ONSNetworkScanCtlr.NetworkAvailableCallBack mNetworkAvailableCallBack = 122 new ONSNetworkScanCtlr.NetworkAvailableCallBack() { 123 @Override 124 public void onNetworkAvailability(List<CellInfo> results) { 125 int subId = retrieveBestSubscription(results); 126 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 127 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 128 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 129 synchronized (mLock) { 130 mNetworkScanCallback = null; 131 } 132 return; 133 } 134 135 /* stop scanning further */ 136 mNetworkScanCtlr.stopNetworkScan(); 137 handleNetworkScanResult(subId); 138 } 139 140 @Override 141 public void onError(int error) { 142 log("Network scan failed with error " + error); 143 synchronized (mLock) { 144 if (mIsEnabled && mAvailableNetworkInfos != null 145 && mAvailableNetworkInfos.size() > 0) { 146 handleNetworkScanResult(mAvailableNetworkInfos.get(0).getSubId()); 147 } else { 148 if (mNetworkScanCallback != null) { 149 if (mIsEnabled) { 150 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 151 TelephonyManager 152 .UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 153 } else { 154 if (Compatibility.isChangeEnabled( 155 OpportunisticNetworkService 156 .CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 157 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 158 TelephonyManager 159 .UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED); 160 } else { 161 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 162 TelephonyManager 163 .UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); 164 } 165 } 166 mNetworkScanCallback = null; 167 } 168 } 169 } 170 } 171 172 private void handleNetworkScanResult(int subId) { 173 /* if subscription is already active, just enable modem */ 174 if (mSubscriptionManager.isActiveSubId(subId)) { 175 if (enableModem(subId, true)) { 176 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 177 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 178 } else { 179 if (Compatibility.isChangeEnabled( 180 OpportunisticNetworkService 181 .CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 182 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 183 TelephonyManager 184 .UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 185 } else { 186 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 187 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 188 } 189 } 190 mProfileSelectionCallback.onProfileSelectionDone(); 191 synchronized (mLock) { 192 mNetworkScanCallback = null; 193 mAvailableNetworkInfos = null; 194 } 195 } else { 196 logDebug("switch to sub:" + subId); 197 switchToSubscription(subId, getAvailableESIMPortIndex()); 198 } 199 } 200 }; 201 202 @VisibleForTesting 203 protected SubscriptionManager.OnOpportunisticSubscriptionsChangedListener 204 mProfileChangeListener = 205 new SubscriptionManager.OnOpportunisticSubscriptionsChangedListener() { 206 @Override 207 public void onOpportunisticSubscriptionsChanged() { 208 logDebug("onOpportunisticSubscriptionsChanged."); 209 mHandler.sendEmptyMessage(MSG_PROFILE_UPDATE); 210 } 211 }; 212 213 /** 214 * interface call back to confirm profile selection 215 */ 216 public interface ONSProfileSelectionCallback { 217 218 /** 219 * interface call back to confirm profile selection 220 */ onProfileSelectionDone()221 void onProfileSelectionDone(); 222 } 223 224 class SortSubInfo implements Comparator<SubscriptionInfo> 225 { 226 // Used for sorting in ascending order of sub id compare(SubscriptionInfo a, SubscriptionInfo b)227 public int compare(SubscriptionInfo a, SubscriptionInfo b) 228 { 229 return a.getSubscriptionId() - b.getSubscriptionId(); 230 } 231 } 232 233 class SortAvailableNetworks implements Comparator<AvailableNetworkInfo> 234 { 235 // Used for sorting in ascending order of sub id compare(AvailableNetworkInfo a, AvailableNetworkInfo b)236 public int compare(AvailableNetworkInfo a, AvailableNetworkInfo b) 237 { 238 return a.getSubId() - b.getSubId(); 239 } 240 } 241 242 class SortAvailableNetworksInPriority implements Comparator<AvailableNetworkInfo> 243 { 244 // Used for sorting in descending order of priority (ascending order of priority numbers) compare(AvailableNetworkInfo a, AvailableNetworkInfo b)245 public int compare(AvailableNetworkInfo a, AvailableNetworkInfo b) 246 { 247 return a.getPriority() - b.getPriority(); 248 } 249 } 250 251 /** 252 * ONSProfileSelector constructor 253 * @param c context 254 * @param profileSelectionCallback callback to be called once selection is done 255 */ ONSProfileSelector(Context c, ONSProfileSelectionCallback profileSelectionCallback)256 public ONSProfileSelector(Context c, ONSProfileSelectionCallback profileSelectionCallback) { 257 init(c, profileSelectionCallback); 258 log("ONSProfileSelector init complete"); 259 } 260 getSignalLevel(CellInfo cellInfo)261 private int getSignalLevel(CellInfo cellInfo) { 262 if (cellInfo != null) { 263 return cellInfo.getCellSignalStrength().getLevel(); 264 } else { 265 return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 266 } 267 } 268 269 @VisibleForTesting getMcc(CellInfo cellInfo)270 protected String getMcc(CellInfo cellInfo) { 271 String mcc = ""; 272 if (cellInfo instanceof CellInfoLte) { 273 mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString(); 274 } 275 else if (cellInfo instanceof CellInfoNr) { 276 mcc = ((CellInfoNr) cellInfo).getCellIdentity().getMccString(); 277 } 278 279 return mcc; 280 } 281 282 @VisibleForTesting getMnc(CellInfo cellInfo)283 protected String getMnc(CellInfo cellInfo) { 284 String mnc = ""; 285 if (cellInfo instanceof CellInfoLte) { 286 mnc = ((CellInfoLte) cellInfo).getCellIdentity().getMncString(); 287 } 288 else if (cellInfo instanceof CellInfoNr) { 289 mnc = ((CellInfoNr) cellInfo).getCellIdentity().getMncString(); 290 } 291 292 return mnc; 293 } 294 getSubIdUsingAvailableNetworks(String mcc, String mnc, int priorityLevel)295 private int getSubIdUsingAvailableNetworks(String mcc, String mnc, int priorityLevel) { 296 String mccMnc = mcc + mnc; 297 synchronized (mLock) { 298 if (mAvailableNetworkInfos != null) { 299 for (AvailableNetworkInfo availableNetworkInfo : mAvailableNetworkInfos) { 300 if (availableNetworkInfo.getPriority() != priorityLevel) { 301 continue; 302 } 303 for (String availableMccMnc : availableNetworkInfo.getMccMncs()) { 304 if (TextUtils.equals(availableMccMnc, mccMnc)) { 305 return availableNetworkInfo.getSubId(); 306 } 307 } 308 } 309 } 310 } 311 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 312 } 313 getOpprotunisticSubInfo(int subId)314 public SubscriptionInfo getOpprotunisticSubInfo(int subId) { 315 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 316 return null; 317 } 318 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 319 if (subscriptionInfo.getSubscriptionId() == subId) { 320 return subscriptionInfo; 321 } 322 } 323 return null; 324 } 325 isOpprotunisticSub(int subId)326 public boolean isOpprotunisticSub(int subId) { 327 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 328 return false; 329 } 330 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 331 if (subscriptionInfo.getSubscriptionId() == subId) { 332 return true; 333 } 334 } 335 return false; 336 } 337 hasOpprotunisticSub(List<AvailableNetworkInfo> availableNetworks)338 public boolean hasOpprotunisticSub(List<AvailableNetworkInfo> availableNetworks) { 339 if ((availableNetworks == null) || (availableNetworks.size() == 0)) { 340 return false; 341 } 342 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 343 return false; 344 } 345 346 for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) { 347 if (!isOpprotunisticSub(availableNetworkInfo.getSubId())) { 348 return false; 349 } 350 } 351 return true; 352 } 353 isAvtiveSub(int subId)354 private boolean isAvtiveSub(int subId) { 355 return mSubscriptionManager.isActiveSubscriptionId(subId); 356 } 357 358 private HashMap<Integer, IUpdateAvailableNetworksCallback> callbackStubs = new HashMap<>(); 359 switchToSubscription(int subId, int availableSIMPortIndex)360 private void switchToSubscription(int subId, int availableSIMPortIndex) { 361 Intent callbackIntent = new Intent(ACTION_SUB_SWITCH); 362 callbackIntent.setClass(mContext, OpportunisticNetworkService.class); 363 updateToken(); 364 callbackIntent.putExtra("sequenceId", mSequenceId); 365 callbackIntent.putExtra("subId", subId); 366 mSubId = subId; 367 PendingIntent replyIntent = PendingIntent.getService(mContext, 368 1, callbackIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); 369 if (availableSIMPortIndex == TelephonyManager.INVALID_PORT_INDEX) { 370 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 371 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE); 372 return; 373 } 374 mEuiccManager.switchToSubscription(subId, availableSIMPortIndex, replyIntent); 375 } 376 377 @VisibleForTesting getAvailableESIMPortIndex()378 protected int getAvailableESIMPortIndex() { 379 //Check if an opportunistic subscription is already active. If yes then, use the same port. 380 List<SubscriptionInfo> subscriptionInfos = mSubscriptionManager 381 .getCompleteActiveSubscriptionInfoList(); 382 if (subscriptionInfos != null) { 383 logDebug("[getAvailableESIMPortIndex] subscriptionInfos size:" 384 + subscriptionInfos.size()); 385 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 386 if (subscriptionInfo.isEmbedded() && subscriptionInfo.isOpportunistic()) { 387 return subscriptionInfo.getPortIndex(); 388 } 389 } 390 } 391 392 //Look for available port. 393 for (UiccCardInfo uiccCardInfo : mTelephonyManager.getUiccCardsInfo()) { 394 logDebug("[getAvailableESIMPortIndex] CardInfo: " + uiccCardInfo.toString()); 395 if (!uiccCardInfo.isEuicc()) { 396 continue; 397 } 398 399 EuiccManager euiccManager = mEuiccManager.createForCardId(uiccCardInfo.getCardId()); 400 for (UiccPortInfo uiccPortInfo : uiccCardInfo.getPorts()) { 401 logDebug("[getAvailableESIMPortIndex] PortInfo: " + uiccPortInfo.toString()); 402 //Port is available if no profiles enabled on it. 403 if (euiccManager.isSimPortAvailable(uiccPortInfo.getPortIndex())) { 404 return uiccPortInfo.getPortIndex(); 405 } 406 } 407 } 408 409 logDebug("[getAvailableESIMPortIndex] No Port is available."); 410 return TelephonyManager.INVALID_PORT_INDEX; 411 } 412 onSubSwitchComplete(Intent intent)413 void onSubSwitchComplete(Intent intent) { 414 int sequenceId = intent.getIntExtra("sequenceId", INVALID_SEQUENCE_ID); 415 int subId = intent.getIntExtra("subId", 416 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 417 logDebug("ACTION_SUB_SWITCH sequenceId: " + sequenceId 418 + " mSequenceId: " + mSequenceId 419 + " mSubId: " + mSubId 420 + " subId: " + subId); 421 Message message = Message.obtain(mHandler, MSG_SUB_SWITCH_COMPLETE, subId); 422 message.sendToTarget(); 423 } 424 onSubSwitchComplete(int subId)425 private void onSubSwitchComplete(int subId) { 426 /* Ignore if this is callback for an older request */ 427 if (mSubId != subId) { 428 return; 429 } 430 431 if (enableModem(subId, true)) { 432 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 433 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 434 } else { 435 if (Compatibility.isChangeEnabled( 436 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 437 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 438 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 439 } else { 440 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 441 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 442 } 443 } 444 mProfileSelectionCallback.onProfileSelectionDone(); 445 mNetworkScanCallback = null; 446 mAvailableNetworkInfos = null; 447 } 448 updateToken()449 private void updateToken() { 450 synchronized (mLock) { 451 mSequenceId++; 452 } 453 } 454 getFilteredAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, List<SubscriptionInfo> subscriptionInfoList)455 private ArrayList<AvailableNetworkInfo> getFilteredAvailableNetworks( 456 ArrayList<AvailableNetworkInfo> availableNetworks, 457 List<SubscriptionInfo> subscriptionInfoList) { 458 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 459 new ArrayList<AvailableNetworkInfo>(); 460 461 /* instead of checking each element of a list every element of the other, sort them in 462 the order of sub id and compare to improve the filtering performance. */ 463 Collections.sort(subscriptionInfoList, new SortSubInfo()); 464 Collections.sort(availableNetworks, new SortAvailableNetworks()); 465 int availableNetworksIndex = 0; 466 int subscriptionInfoListIndex = 0; 467 SubscriptionInfo subscriptionInfo; 468 AvailableNetworkInfo availableNetwork; 469 470 while (availableNetworksIndex < availableNetworks.size() 471 && subscriptionInfoListIndex < subscriptionInfoList.size()) { 472 subscriptionInfo = subscriptionInfoList.get(subscriptionInfoListIndex); 473 availableNetwork = availableNetworks.get(availableNetworksIndex); 474 if (subscriptionInfo.getSubscriptionId() == availableNetwork.getSubId()) { 475 filteredAvailableNetworks.add(availableNetwork); 476 subscriptionInfoListIndex++; 477 availableNetworksIndex++; 478 } else if (subscriptionInfo.getSubscriptionId() < availableNetwork.getSubId()) { 479 subscriptionInfoListIndex++; 480 } else { 481 availableNetworksIndex++; 482 } 483 } 484 return filteredAvailableNetworks; 485 } 486 isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, ArrayList<AvailableNetworkInfo> availableNetworks2)487 private boolean isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, 488 ArrayList<AvailableNetworkInfo> availableNetworks2) { 489 if ((availableNetworks1 == null) || (availableNetworks2 == null)) { 490 return false; 491 } 492 return new HashSet<>(availableNetworks1).equals(new HashSet<>(availableNetworks2)); 493 } 494 sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result)495 private void sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, 496 int result) { 497 if (callback == null) { 498 log("callback is null"); 499 return; 500 } 501 try { 502 callback.onComplete(result); 503 } catch (RemoteException exception) { 504 log("RemoteException " + exception); 505 } 506 } 507 checkProfileUpdate(Object[] objects)508 private void checkProfileUpdate(Object[] objects) { 509 ArrayList<AvailableNetworkInfo> availableNetworks = 510 (ArrayList<AvailableNetworkInfo>) objects[0]; 511 IUpdateAvailableNetworksCallback callbackStub = 512 (IUpdateAvailableNetworksCallback) objects[1]; 513 if (mOppSubscriptionInfos == null) { 514 logDebug("null subscription infos"); 515 if (Compatibility.isChangeEnabled( 516 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 517 sendUpdateNetworksCallbackHelper(callbackStub, 518 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 519 } else { 520 sendUpdateNetworksCallbackHelper(callbackStub, 521 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 522 } 523 return; 524 } 525 526 /* Check if ports are available on the embedded slot */ 527 int availSIMPortIndex = getAvailableESIMPortIndex(); 528 if (availSIMPortIndex == TelephonyManager.INVALID_PORT_INDEX) { 529 logDebug("SIM port not available."); 530 sendUpdateNetworksCallbackHelper(callbackStub, 531 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE); 532 return; 533 } 534 535 if (isSame(availableNetworks, mAvailableNetworkInfos)) { 536 logDebug("received duplicate requests"); 537 /* If we receive same request more than once, send abort response for earlier one 538 and send actual response for the latest callback. 539 */ 540 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 541 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 542 mNetworkScanCallback = callbackStub; 543 return; 544 } 545 546 stopProfileScanningPrecedure(); 547 mIsEnabled = true; 548 mAvailableNetworkInfos = availableNetworks; 549 /* sort in the order of priority */ 550 Collections.sort(mAvailableNetworkInfos, new SortAvailableNetworksInPriority()); 551 logDebug("availableNetworks: " + availableNetworks); 552 553 if (mOppSubscriptionInfos.size() > 0) { 554 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 555 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 556 getFilteredAvailableNetworks((ArrayList<AvailableNetworkInfo>)availableNetworks, 557 mOppSubscriptionInfos); 558 if ((filteredAvailableNetworks.size() == 1) 559 && ((filteredAvailableNetworks.get(0).getMccMncs() == null) 560 || (filteredAvailableNetworks.get(0).getMccMncs().size() == 0))) { 561 /* if subscription is not active, activate the sub */ 562 if (!mSubscriptionManager.isActiveSubId(filteredAvailableNetworks.get(0).getSubId())) { 563 mNetworkScanCallback = callbackStub; 564 switchToSubscription(filteredAvailableNetworks.get(0).getSubId(), 565 availSIMPortIndex); 566 } else { 567 if (enableModem(filteredAvailableNetworks.get(0).getSubId(), true)) { 568 sendUpdateNetworksCallbackHelper(callbackStub, 569 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 570 } else { 571 if (Compatibility.isChangeEnabled( 572 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 573 sendUpdateNetworksCallbackHelper(callbackStub, 574 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 575 } else { 576 sendUpdateNetworksCallbackHelper(callbackStub, 577 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 578 } 579 } 580 mProfileSelectionCallback.onProfileSelectionDone(); 581 mAvailableNetworkInfos = null; 582 } 583 } else { 584 mNetworkScanCallback = callbackStub; 585 /* start scan immediately */ 586 mNetworkScanCtlr.startFastNetworkScan(filteredAvailableNetworks); 587 } 588 } else if (mOppSubscriptionInfos.size() == 0) { 589 if (Compatibility.isChangeEnabled( 590 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 591 sendUpdateNetworksCallbackHelper(callbackStub, 592 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 593 } else { 594 sendUpdateNetworksCallbackHelper(callbackStub, 595 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 596 } 597 /* check if no profile */ 598 logDebug("stopping scan"); 599 mNetworkScanCtlr.stopNetworkScan(); 600 } 601 } 602 isActiveSub(int subId)603 private boolean isActiveSub(int subId) { 604 List<SubscriptionInfo> subscriptionInfos = 605 mSubscriptionManager.getActiveSubscriptionInfoList(false); 606 if (subscriptionInfos == null) { 607 return false; 608 } 609 610 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 611 if (subscriptionInfo.getSubscriptionId() == subId) { 612 return true; 613 } 614 } 615 616 return false; 617 } 618 619 @VisibleForTesting retrieveBestSubscription(List<CellInfo> results)620 protected int retrieveBestSubscription(List<CellInfo> results) { 621 /* sort the results according to signal strength level */ 622 Collections.sort(results, new Comparator<CellInfo>() { 623 @Override 624 public int compare(CellInfo cellInfo1, CellInfo cellInfo2) { 625 return getSignalLevel(cellInfo1) - getSignalLevel(cellInfo2); 626 } 627 }); 628 629 for (int level = PRIORITY_HIGH; level < PRIORITY_LOW; level++) { 630 for (CellInfo result : results) { 631 /* get subscription id for the best network scan result */ 632 int subId = getSubIdUsingAvailableNetworks(getMcc(result), getMnc(result), level); 633 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 634 return subId; 635 } 636 } 637 } 638 639 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 640 } 641 isOpportunisticSubEmbedded( ArrayList<AvailableNetworkInfo> availableNetworks)642 private boolean isOpportunisticSubEmbedded( 643 ArrayList<AvailableNetworkInfo> availableNetworks) { 644 List<SubscriptionInfo> subscriptionInfos = 645 mSubscriptionManager.getOpportunisticSubscriptions(); 646 if (subscriptionInfos == null) { 647 return false; 648 } 649 for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) { 650 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 651 if (subscriptionInfo.getSubscriptionId() == availableNetworkInfo.getSubId() 652 && subscriptionInfo.isEmbedded()) { 653 return true; 654 } 655 } 656 } 657 658 return false; 659 } 660 getActiveOpportunisticSubId()661 private int getActiveOpportunisticSubId() { 662 List<SubscriptionInfo> subscriptionInfos = 663 mSubscriptionManager.getActiveSubscriptionInfoList(false); 664 if (subscriptionInfos == null) { 665 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 666 } 667 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 668 if (subscriptionInfo.isOpportunistic()) { 669 return subscriptionInfo.getSubscriptionId(); 670 } 671 } 672 673 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 674 } 675 disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub)676 private void disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub) { 677 int subId = getActiveOpportunisticSubId(); 678 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 679 if (Compatibility.isChangeEnabled( 680 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 681 sendUpdateNetworksCallbackHelper(callbackStub, 682 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 683 } else { 684 sendUpdateNetworksCallbackHelper(callbackStub, 685 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 686 } 687 return; 688 } 689 if (enableModem(subId, false)) { 690 sendUpdateNetworksCallbackHelper(callbackStub, 691 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 692 } else { 693 if (Compatibility.isChangeEnabled( 694 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 695 sendUpdateNetworksCallbackHelper(callbackStub, 696 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL); 697 } else { 698 sendUpdateNetworksCallbackHelper(callbackStub, 699 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 700 } 701 } 702 } 703 enableModem(int subId, boolean enable)704 private boolean enableModem(int subId, boolean enable) { 705 SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 706 if (info == null) { 707 // Subscription is not active. Do nothing. 708 return false; 709 } 710 711 // If disabling modem for opportunistic sub, make sure to switch data back to default sub. 712 if (!enable) { 713 if (mSubscriptionManager.getPreferredDataSubscriptionId() == subId) { 714 selectProfileForData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null); 715 } 716 } 717 int phoneId = info.getSimSlotIndex(); 718 /* Todo: b/135067156 719 * Reenable this code once 135067156 is fixed 720 if (mSubscriptionBoundTelephonyManager.isModemEnabledForSlot(phoneId) == enable) { 721 logDebug("modem is already enabled "); 722 return true; 723 } */ 724 725 return mSubscriptionBoundTelephonyManager.enableModemForSlot(phoneId, enable); 726 } 727 stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub)728 private void stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub) { 729 stopProfileScanningPrecedure(); 730 logDebug("stopProfileSelection"); 731 disableOpportunisticModem(callbackStub); 732 } 733 stopProfileScanningPrecedure()734 private void stopProfileScanningPrecedure() { 735 synchronized (mLock) { 736 if (mNetworkScanCallback != null) { 737 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 738 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 739 mNetworkScanCallback = null; 740 } 741 mNetworkScanCtlr.stopNetworkScan(); 742 743 mAvailableNetworkInfos = null; 744 mIsEnabled = false; 745 } 746 } 747 containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks)748 public boolean containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 749 if (mOppSubscriptionInfos == null) { 750 logDebug("received null subscription infos"); 751 return false; 752 } 753 754 if (mOppSubscriptionInfos.size() > 0) { 755 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 756 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 757 getFilteredAvailableNetworks( 758 (ArrayList<AvailableNetworkInfo>)availableNetworks, mOppSubscriptionInfos); 759 if (filteredAvailableNetworks.size() > 0) { 760 return true; 761 } 762 } 763 764 return false; 765 } 766 containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks)767 public boolean containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 768 if (mStandaloneOppSubInfos == null) { 769 logDebug("received null subscription infos"); 770 return false; 771 } 772 if (mStandaloneOppSubInfos.size() > 0) { 773 logDebug("Standalone opportunistic subInfos size " + mStandaloneOppSubInfos.size()); 774 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 775 getFilteredAvailableNetworks( 776 (ArrayList<AvailableNetworkInfo>) availableNetworks, 777 mStandaloneOppSubInfos); 778 if (filteredAvailableNetworks.size() > 0) { 779 return true; 780 } 781 } 782 return false; 783 } 784 isOpportunisticSubActive()785 public boolean isOpportunisticSubActive() { 786 if (mOppSubscriptionInfos == null) { 787 logDebug("received null subscription infos"); 788 return false; 789 } 790 791 if (mOppSubscriptionInfos.size() > 0) { 792 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 793 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 794 if (mSubscriptionManager.isActiveSubId(subscriptionInfo.getSubscriptionId())) { 795 return true; 796 } 797 } 798 } 799 return false; 800 } 801 startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub)802 public void startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, 803 IUpdateAvailableNetworksCallback callbackStub) { 804 logDebug("startProfileSelection availableNetworks: " + availableNetworks); 805 if (availableNetworks == null || availableNetworks.size() == 0) { 806 if (callbackStub != null) { 807 sendUpdateNetworksCallbackHelper(callbackStub, 808 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 809 } 810 return; 811 } 812 Object[] objects = new Object[]{availableNetworks, callbackStub}; 813 Message message = Message.obtain(mHandler, MSG_START_PROFILE_SELECTION, objects); 814 message.sendToTarget(); 815 } 816 sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result)817 private void sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result) { 818 if (callback == null) return; 819 try { 820 callback.onComplete(result); 821 } catch (RemoteException exception) { 822 log("RemoteException " + exception); 823 } 824 } 825 826 /** 827 * select opportunistic profile for data if passing a valid subId. 828 * @param subId : opportunistic subId or SubscriptionManager.DEFAULT_SUBSCRIPTION_ID if 829 * deselecting previously set preference. 830 */ selectProfileForData(int subId, boolean needValidation, ISetOpportunisticDataCallback callbackStub)831 public void selectProfileForData(int subId, boolean needValidation, 832 ISetOpportunisticDataCallback callbackStub) { 833 if ((subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) 834 || (isOpprotunisticSub(subId) && mSubscriptionManager.isActiveSubId(subId))) { 835 try { 836 mSubscriptionManager.setPreferredDataSubscriptionId(subId, needValidation, 837 mHandler::post, result -> sendSetOpptCallbackHelper(callbackStub, result)); 838 } catch (Exception ex) { 839 log("setPreferredDataSubscriptionId failed. subId=" + subId + ", needValidation=" 840 + needValidation + ", ex=" + ex); 841 if (Compatibility.isChangeEnabled( 842 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 843 sendSetOpptCallbackHelper(callbackStub, 844 TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); 845 } else { 846 sendSetOpptCallbackHelper(callbackStub, 847 TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); 848 } 849 return; 850 } 851 mCurrentDataSubId = subId; 852 } else { 853 log("Inactive sub passed for preferred data " + subId); 854 if (Compatibility.isChangeEnabled( 855 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 856 if (isOpprotunisticSub(subId)) { 857 sendSetOpptCallbackHelper(callbackStub, 858 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 859 } else { 860 sendSetOpptCallbackHelper(callbackStub, 861 TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE); 862 } 863 } else { 864 sendSetOpptCallbackHelper(callbackStub, 865 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 866 } 867 } 868 } 869 getPreferredDataSubscriptionId()870 public int getPreferredDataSubscriptionId() { 871 return mSubscriptionManager.getPreferredDataSubscriptionId(); 872 } 873 874 /** 875 * stop profile selection procedure 876 */ stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub)877 public void stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub) { 878 logDebug("stopProfileSelection"); 879 Message message = Message.obtain(mHandler, MSG_STOP_PROFILE_SELECTION, callbackStub); 880 message.sendToTarget(); 881 } 882 883 @VisibleForTesting updateOpportunisticSubscriptions()884 protected void updateOpportunisticSubscriptions() { 885 synchronized (mLock) { 886 mOppSubscriptionInfos = mSubscriptionManager 887 .getOpportunisticSubscriptions().stream() 888 .filter(subInfo -> subInfo.isGroupDisabled() != true) 889 .collect(Collectors.toList()); 890 if (mOppSubscriptionInfos != null) { 891 mStandaloneOppSubInfos = mOppSubscriptionInfos.stream() 892 .filter(subInfo -> subInfo.getGroupUuid() == null) 893 .collect(Collectors.toList()); 894 } 895 } 896 } 897 enableModemStackForNonOpportunisticSlots()898 private void enableModemStackForNonOpportunisticSlots() { 899 int phoneCount = mTelephonyManager.getPhoneCount(); 900 // Do nothing in single SIM mode. 901 if (phoneCount < 2) return; 902 903 for (int i = 0; i < phoneCount; i++) { 904 boolean hasActiveOpptProfile = false; 905 for (SubscriptionInfo info : mOppSubscriptionInfos) { 906 if (info.getSimSlotIndex() == i) { 907 hasActiveOpptProfile = true; 908 } 909 } 910 // If the slot doesn't have active opportunistic profile anymore, it's back to 911 // DSDS use-case. Make sure the the modem stack is enabled. 912 if (!hasActiveOpptProfile) mTelephonyManager.enableModemForSlot(i, true); 913 } 914 } 915 916 @VisibleForTesting init(Context c, ONSProfileSelectionCallback profileSelectionCallback)917 protected void init(Context c, ONSProfileSelectionCallback profileSelectionCallback) { 918 mContext = c; 919 mSequenceId = START_SEQUENCE_ID; 920 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 921 mProfileSelectionCallback = profileSelectionCallback; 922 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 923 mSubscriptionBoundTelephonyManager = mTelephonyManager.createForSubscriptionId( 924 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 925 mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); 926 mNetworkScanCtlr = new ONSNetworkScanCtlr(mContext, mSubscriptionBoundTelephonyManager, 927 mNetworkAvailableCallBack); 928 mEuiccManager = c.getSystemService(EuiccManager.class); 929 updateOpportunisticSubscriptions(); 930 mThread = new HandlerThread(LOG_TAG); 931 mThread.start(); 932 mHandler = new Handler(mThread.getLooper()) { 933 @Override 934 public void handleMessage(Message msg) { 935 switch (msg.what) { 936 case MSG_PROFILE_UPDATE: 937 synchronized (mLock) { 938 updateOpportunisticSubscriptions(); 939 enableModemStackForNonOpportunisticSlots(); 940 } 941 break; 942 case MSG_START_PROFILE_SELECTION: 943 logDebug("Msg received for profile update"); 944 synchronized (mLock) { 945 checkProfileUpdate((Object[]) msg.obj); 946 } 947 break; 948 case MSG_STOP_PROFILE_SELECTION: 949 logDebug("Msg received to stop profile selection"); 950 synchronized (mLock) { 951 stopProfileSelectionProcess((IUpdateAvailableNetworksCallback) msg.obj); 952 } 953 break; 954 case MSG_SUB_SWITCH_COMPLETE: 955 logDebug("Msg received for sub switch"); 956 synchronized (mLock) { 957 onSubSwitchComplete((int) msg.obj); 958 } 959 break; 960 default: 961 log("invalid message"); 962 break; 963 } 964 } 965 }; 966 /* register for profile update events */ 967 mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener( 968 AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener); 969 } 970 log(String msg)971 private void log(String msg) { 972 Rlog.d(LOG_TAG, msg); 973 } 974 logDebug(String msg)975 private void logDebug(String msg) { 976 if (DBG) { 977 Rlog.d(LOG_TAG, msg); 978 } 979 } 980 } 981