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