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 375 List<SubscriptionInfo> subInfoList = mSubscriptionManager 376 .getAvailableSubscriptionInfoList(); 377 for (SubscriptionInfo subInfo : subInfoList) { 378 if (subId == subInfo.getSubscriptionId()) { 379 EuiccManager euiccManager = mEuiccManager.createForCardId(subInfo.getCardId()); 380 euiccManager.switchToSubscription(subId, availableSIMPortIndex, replyIntent); 381 break; 382 } 383 } 384 } 385 386 @VisibleForTesting getAvailableESIMPortIndex()387 protected int getAvailableESIMPortIndex() { 388 //Check if an opportunistic subscription is already active. If yes then, use the same port. 389 List<SubscriptionInfo> subscriptionInfos = mSubscriptionManager 390 .getCompleteActiveSubscriptionInfoList(); 391 if (subscriptionInfos != null) { 392 logDebug("[getAvailableESIMPortIndex] subscriptionInfos size:" 393 + subscriptionInfos.size()); 394 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 395 if (subscriptionInfo.isEmbedded() && subscriptionInfo.isOpportunistic()) { 396 return subscriptionInfo.getPortIndex(); 397 } 398 } 399 } 400 401 //Look for available port. 402 for (UiccCardInfo uiccCardInfo : mTelephonyManager.getUiccCardsInfo()) { 403 logDebug("[getAvailableESIMPortIndex] CardInfo: " + uiccCardInfo.toString()); 404 if (!uiccCardInfo.isEuicc()) { 405 continue; 406 } 407 408 EuiccManager euiccManager = mEuiccManager.createForCardId(uiccCardInfo.getCardId()); 409 for (UiccPortInfo uiccPortInfo : uiccCardInfo.getPorts()) { 410 logDebug("[getAvailableESIMPortIndex] PortInfo: " + uiccPortInfo.toString()); 411 //Port is available if no profiles enabled on it. 412 if (euiccManager.isSimPortAvailable(uiccPortInfo.getPortIndex())) { 413 return uiccPortInfo.getPortIndex(); 414 } 415 } 416 } 417 418 logDebug("[getAvailableESIMPortIndex] No Port is available."); 419 return TelephonyManager.INVALID_PORT_INDEX; 420 } 421 onSubSwitchComplete(Intent intent)422 void onSubSwitchComplete(Intent intent) { 423 int sequenceId = intent.getIntExtra("sequenceId", INVALID_SEQUENCE_ID); 424 int subId = intent.getIntExtra("subId", 425 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 426 logDebug("ACTION_SUB_SWITCH sequenceId: " + sequenceId 427 + " mSequenceId: " + mSequenceId 428 + " mSubId: " + mSubId 429 + " subId: " + subId); 430 Message message = Message.obtain(mHandler, MSG_SUB_SWITCH_COMPLETE, subId); 431 message.sendToTarget(); 432 } 433 onSubSwitchComplete(int subId)434 private void onSubSwitchComplete(int subId) { 435 /* Ignore if this is callback for an older request */ 436 if (mSubId != subId) { 437 return; 438 } 439 440 if (enableModem(subId, true)) { 441 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 442 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 443 } else { 444 if (Compatibility.isChangeEnabled( 445 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 446 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 447 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 448 } else { 449 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 450 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 451 } 452 } 453 mProfileSelectionCallback.onProfileSelectionDone(); 454 mNetworkScanCallback = null; 455 mAvailableNetworkInfos = null; 456 } 457 updateToken()458 private void updateToken() { 459 synchronized (mLock) { 460 mSequenceId++; 461 } 462 } 463 getFilteredAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, List<SubscriptionInfo> subscriptionInfoList)464 private ArrayList<AvailableNetworkInfo> getFilteredAvailableNetworks( 465 ArrayList<AvailableNetworkInfo> availableNetworks, 466 List<SubscriptionInfo> subscriptionInfoList) { 467 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 468 new ArrayList<AvailableNetworkInfo>(); 469 470 /* instead of checking each element of a list every element of the other, sort them in 471 the order of sub id and compare to improve the filtering performance. */ 472 Collections.sort(subscriptionInfoList, new SortSubInfo()); 473 Collections.sort(availableNetworks, new SortAvailableNetworks()); 474 int availableNetworksIndex = 0; 475 int subscriptionInfoListIndex = 0; 476 SubscriptionInfo subscriptionInfo; 477 AvailableNetworkInfo availableNetwork; 478 479 while (availableNetworksIndex < availableNetworks.size() 480 && subscriptionInfoListIndex < subscriptionInfoList.size()) { 481 subscriptionInfo = subscriptionInfoList.get(subscriptionInfoListIndex); 482 availableNetwork = availableNetworks.get(availableNetworksIndex); 483 if (subscriptionInfo.getSubscriptionId() == availableNetwork.getSubId()) { 484 filteredAvailableNetworks.add(availableNetwork); 485 subscriptionInfoListIndex++; 486 availableNetworksIndex++; 487 } else if (subscriptionInfo.getSubscriptionId() < availableNetwork.getSubId()) { 488 subscriptionInfoListIndex++; 489 } else { 490 availableNetworksIndex++; 491 } 492 } 493 return filteredAvailableNetworks; 494 } 495 isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, ArrayList<AvailableNetworkInfo> availableNetworks2)496 private boolean isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, 497 ArrayList<AvailableNetworkInfo> availableNetworks2) { 498 if ((availableNetworks1 == null) || (availableNetworks2 == null)) { 499 return false; 500 } 501 return new HashSet<>(availableNetworks1).equals(new HashSet<>(availableNetworks2)); 502 } 503 sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result)504 private void sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, 505 int result) { 506 if (callback == null) { 507 log("callback is null"); 508 return; 509 } 510 try { 511 callback.onComplete(result); 512 } catch (RemoteException exception) { 513 log("RemoteException " + exception); 514 } 515 } 516 checkProfileUpdate(Object[] objects)517 private void checkProfileUpdate(Object[] objects) { 518 ArrayList<AvailableNetworkInfo> availableNetworks = 519 (ArrayList<AvailableNetworkInfo>) objects[0]; 520 IUpdateAvailableNetworksCallback callbackStub = 521 (IUpdateAvailableNetworksCallback) objects[1]; 522 if (mOppSubscriptionInfos == null) { 523 logDebug("null subscription infos"); 524 if (Compatibility.isChangeEnabled( 525 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 526 sendUpdateNetworksCallbackHelper(callbackStub, 527 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 528 } else { 529 sendUpdateNetworksCallbackHelper(callbackStub, 530 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 531 } 532 return; 533 } 534 535 /* Check if ports are available on the embedded slot */ 536 int availSIMPortIndex = getAvailableESIMPortIndex(); 537 if (availSIMPortIndex == TelephonyManager.INVALID_PORT_INDEX) { 538 logDebug("SIM port not available."); 539 sendUpdateNetworksCallbackHelper(callbackStub, 540 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE); 541 return; 542 } 543 544 if (isSame(availableNetworks, mAvailableNetworkInfos)) { 545 logDebug("received duplicate requests"); 546 /* If we receive same request more than once, send abort response for earlier one 547 and send actual response for the latest callback. 548 */ 549 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 550 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 551 mNetworkScanCallback = callbackStub; 552 return; 553 } 554 555 stopProfileScanningPrecedure(); 556 mIsEnabled = true; 557 mAvailableNetworkInfos = availableNetworks; 558 /* sort in the order of priority */ 559 Collections.sort(mAvailableNetworkInfos, new SortAvailableNetworksInPriority()); 560 logDebug("availableNetworks: " + availableNetworks); 561 562 if (mOppSubscriptionInfos.size() > 0) { 563 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 564 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 565 getFilteredAvailableNetworks((ArrayList<AvailableNetworkInfo>)availableNetworks, 566 mOppSubscriptionInfos); 567 if ((filteredAvailableNetworks.size() == 1) 568 && ((filteredAvailableNetworks.get(0).getMccMncs() == null) 569 || (filteredAvailableNetworks.get(0).getMccMncs().size() == 0))) { 570 /* if subscription is not active, activate the sub */ 571 if (!mSubscriptionManager.isActiveSubId(filteredAvailableNetworks.get(0).getSubId())) { 572 mNetworkScanCallback = callbackStub; 573 switchToSubscription(filteredAvailableNetworks.get(0).getSubId(), 574 availSIMPortIndex); 575 } else { 576 if (enableModem(filteredAvailableNetworks.get(0).getSubId(), true)) { 577 sendUpdateNetworksCallbackHelper(callbackStub, 578 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 579 } else { 580 if (Compatibility.isChangeEnabled( 581 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 582 sendUpdateNetworksCallbackHelper(callbackStub, 583 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 584 } else { 585 sendUpdateNetworksCallbackHelper(callbackStub, 586 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 587 } 588 } 589 mProfileSelectionCallback.onProfileSelectionDone(); 590 mAvailableNetworkInfos = null; 591 } 592 } else { 593 mNetworkScanCallback = callbackStub; 594 /* start scan immediately */ 595 mNetworkScanCtlr.startFastNetworkScan(filteredAvailableNetworks); 596 } 597 } else if (mOppSubscriptionInfos.size() == 0) { 598 if (Compatibility.isChangeEnabled( 599 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 600 sendUpdateNetworksCallbackHelper(callbackStub, 601 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 602 } else { 603 sendUpdateNetworksCallbackHelper(callbackStub, 604 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 605 } 606 /* check if no profile */ 607 logDebug("stopping scan"); 608 mNetworkScanCtlr.stopNetworkScan(); 609 } 610 } 611 isActiveSub(int subId)612 private boolean isActiveSub(int subId) { 613 List<SubscriptionInfo> subscriptionInfos = 614 mSubscriptionManager.getActiveSubscriptionInfoList(false); 615 if (subscriptionInfos == null) { 616 return false; 617 } 618 619 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 620 if (subscriptionInfo.getSubscriptionId() == subId) { 621 return true; 622 } 623 } 624 625 return false; 626 } 627 628 @VisibleForTesting retrieveBestSubscription(List<CellInfo> results)629 protected int retrieveBestSubscription(List<CellInfo> results) { 630 /* sort the results according to signal strength level */ 631 Collections.sort(results, new Comparator<CellInfo>() { 632 @Override 633 public int compare(CellInfo cellInfo1, CellInfo cellInfo2) { 634 return getSignalLevel(cellInfo1) - getSignalLevel(cellInfo2); 635 } 636 }); 637 638 for (int level = PRIORITY_HIGH; level < PRIORITY_LOW; level++) { 639 for (CellInfo result : results) { 640 /* get subscription id for the best network scan result */ 641 int subId = getSubIdUsingAvailableNetworks(getMcc(result), getMnc(result), level); 642 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 643 return subId; 644 } 645 } 646 } 647 648 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 649 } 650 isOpportunisticSubEmbedded( ArrayList<AvailableNetworkInfo> availableNetworks)651 private boolean isOpportunisticSubEmbedded( 652 ArrayList<AvailableNetworkInfo> availableNetworks) { 653 List<SubscriptionInfo> subscriptionInfos = 654 mSubscriptionManager.getOpportunisticSubscriptions(); 655 if (subscriptionInfos == null) { 656 return false; 657 } 658 for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) { 659 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 660 if (subscriptionInfo.getSubscriptionId() == availableNetworkInfo.getSubId() 661 && subscriptionInfo.isEmbedded()) { 662 return true; 663 } 664 } 665 } 666 667 return false; 668 } 669 getActiveOpportunisticSubId()670 private int getActiveOpportunisticSubId() { 671 List<SubscriptionInfo> subscriptionInfos = 672 mSubscriptionManager.getActiveSubscriptionInfoList(false); 673 if (subscriptionInfos == null) { 674 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 675 } 676 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 677 if (subscriptionInfo.isOpportunistic()) { 678 return subscriptionInfo.getSubscriptionId(); 679 } 680 } 681 682 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 683 } 684 disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub)685 private void disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub) { 686 int subId = getActiveOpportunisticSubId(); 687 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 688 if (Compatibility.isChangeEnabled( 689 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 690 sendUpdateNetworksCallbackHelper(callbackStub, 691 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 692 } else { 693 sendUpdateNetworksCallbackHelper(callbackStub, 694 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 695 } 696 return; 697 } 698 if (enableModem(subId, false)) { 699 sendUpdateNetworksCallbackHelper(callbackStub, 700 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 701 } else { 702 if (Compatibility.isChangeEnabled( 703 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 704 sendUpdateNetworksCallbackHelper(callbackStub, 705 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL); 706 } else { 707 sendUpdateNetworksCallbackHelper(callbackStub, 708 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 709 } 710 } 711 } 712 enableModem(int subId, boolean enable)713 private boolean enableModem(int subId, boolean enable) { 714 SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 715 if (info == null) { 716 // Subscription is not active. Do nothing. 717 return false; 718 } 719 720 // If disabling modem for opportunistic sub, make sure to switch data back to default sub. 721 if (!enable) { 722 if (mSubscriptionManager.getPreferredDataSubscriptionId() == subId) { 723 selectProfileForData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null); 724 } 725 } 726 int phoneId = info.getSimSlotIndex(); 727 /* Todo: b/135067156 728 * Reenable this code once 135067156 is fixed 729 if (mSubscriptionBoundTelephonyManager.isModemEnabledForSlot(phoneId) == enable) { 730 logDebug("modem is already enabled "); 731 return true; 732 } */ 733 734 return mSubscriptionBoundTelephonyManager.enableModemForSlot(phoneId, enable); 735 } 736 stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub)737 private void stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub) { 738 stopProfileScanningPrecedure(); 739 logDebug("stopProfileSelection"); 740 disableOpportunisticModem(callbackStub); 741 } 742 stopProfileScanningPrecedure()743 private void stopProfileScanningPrecedure() { 744 synchronized (mLock) { 745 if (mNetworkScanCallback != null) { 746 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 747 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 748 mNetworkScanCallback = null; 749 } 750 mNetworkScanCtlr.stopNetworkScan(); 751 752 mAvailableNetworkInfos = null; 753 mIsEnabled = false; 754 } 755 } 756 containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks)757 public boolean containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 758 if (mOppSubscriptionInfos == null) { 759 logDebug("received null subscription infos"); 760 return false; 761 } 762 763 if (mOppSubscriptionInfos.size() > 0) { 764 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 765 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 766 getFilteredAvailableNetworks( 767 (ArrayList<AvailableNetworkInfo>)availableNetworks, mOppSubscriptionInfos); 768 if (filteredAvailableNetworks.size() > 0) { 769 return true; 770 } 771 } 772 773 return false; 774 } 775 containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks)776 public boolean containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 777 if (mStandaloneOppSubInfos == null) { 778 logDebug("received null subscription infos"); 779 return false; 780 } 781 if (mStandaloneOppSubInfos.size() > 0) { 782 logDebug("Standalone opportunistic subInfos size " + mStandaloneOppSubInfos.size()); 783 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 784 getFilteredAvailableNetworks( 785 (ArrayList<AvailableNetworkInfo>) availableNetworks, 786 mStandaloneOppSubInfos); 787 if (filteredAvailableNetworks.size() > 0) { 788 return true; 789 } 790 } 791 return false; 792 } 793 isOpportunisticSubActive()794 public boolean isOpportunisticSubActive() { 795 if (mOppSubscriptionInfos == null) { 796 logDebug("received null subscription infos"); 797 return false; 798 } 799 800 if (mOppSubscriptionInfos.size() > 0) { 801 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 802 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 803 if (mSubscriptionManager.isActiveSubId(subscriptionInfo.getSubscriptionId())) { 804 return true; 805 } 806 } 807 } 808 return false; 809 } 810 startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub)811 public void startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, 812 IUpdateAvailableNetworksCallback callbackStub) { 813 logDebug("startProfileSelection availableNetworks: " + availableNetworks); 814 if (availableNetworks == null || availableNetworks.size() == 0) { 815 if (callbackStub != null) { 816 sendUpdateNetworksCallbackHelper(callbackStub, 817 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 818 } 819 return; 820 } 821 Object[] objects = new Object[]{availableNetworks, callbackStub}; 822 Message message = Message.obtain(mHandler, MSG_START_PROFILE_SELECTION, objects); 823 message.sendToTarget(); 824 } 825 sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result)826 private void sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result) { 827 if (callback == null) return; 828 try { 829 callback.onComplete(result); 830 } catch (RemoteException exception) { 831 log("RemoteException " + exception); 832 } 833 } 834 835 /** 836 * select opportunistic profile for data if passing a valid subId. 837 * @param subId : opportunistic subId or SubscriptionManager.DEFAULT_SUBSCRIPTION_ID if 838 * deselecting previously set preference. 839 */ selectProfileForData(int subId, boolean needValidation, ISetOpportunisticDataCallback callbackStub)840 public void selectProfileForData(int subId, boolean needValidation, 841 ISetOpportunisticDataCallback callbackStub) { 842 if ((subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) 843 || (isOpprotunisticSub(subId) && mSubscriptionManager.isActiveSubId(subId))) { 844 try { 845 mSubscriptionManager.setPreferredDataSubscriptionId(subId, needValidation, 846 mHandler::post, result -> sendSetOpptCallbackHelper(callbackStub, result)); 847 } catch (Exception ex) { 848 log("setPreferredDataSubscriptionId failed. subId=" + subId + ", needValidation=" 849 + needValidation + ", ex=" + ex); 850 if (Compatibility.isChangeEnabled( 851 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 852 sendSetOpptCallbackHelper(callbackStub, 853 TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); 854 } else { 855 sendSetOpptCallbackHelper(callbackStub, 856 TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); 857 } 858 return; 859 } 860 mCurrentDataSubId = subId; 861 } else { 862 log("Inactive sub passed for preferred data " + subId); 863 if (Compatibility.isChangeEnabled( 864 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 865 if (isOpprotunisticSub(subId)) { 866 sendSetOpptCallbackHelper(callbackStub, 867 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 868 } else { 869 sendSetOpptCallbackHelper(callbackStub, 870 TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE); 871 } 872 } else { 873 sendSetOpptCallbackHelper(callbackStub, 874 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 875 } 876 } 877 } 878 getPreferredDataSubscriptionId()879 public int getPreferredDataSubscriptionId() { 880 return mSubscriptionManager.getPreferredDataSubscriptionId(); 881 } 882 883 /** 884 * stop profile selection procedure 885 */ stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub)886 public void stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub) { 887 logDebug("stopProfileSelection"); 888 Message message = Message.obtain(mHandler, MSG_STOP_PROFILE_SELECTION, callbackStub); 889 message.sendToTarget(); 890 } 891 892 @VisibleForTesting updateOpportunisticSubscriptions()893 protected void updateOpportunisticSubscriptions() { 894 synchronized (mLock) { 895 mOppSubscriptionInfos = mSubscriptionManager 896 .getOpportunisticSubscriptions().stream() 897 .filter(subInfo -> subInfo.isGroupDisabled() != true) 898 .collect(Collectors.toList()); 899 if (mOppSubscriptionInfos != null) { 900 mStandaloneOppSubInfos = mOppSubscriptionInfos.stream() 901 .filter(subInfo -> subInfo.getGroupUuid() == null) 902 .collect(Collectors.toList()); 903 } 904 } 905 } 906 enableModemStackForNonOpportunisticSlots()907 private void enableModemStackForNonOpportunisticSlots() { 908 int phoneCount = mTelephonyManager.getPhoneCount(); 909 // Do nothing in single SIM mode. 910 if (phoneCount < 2) return; 911 912 for (int i = 0; i < phoneCount; i++) { 913 boolean hasActiveOpptProfile = false; 914 for (SubscriptionInfo info : mOppSubscriptionInfos) { 915 if (info.getSimSlotIndex() == i) { 916 hasActiveOpptProfile = true; 917 } 918 } 919 // If the slot doesn't have active opportunistic profile anymore, it's back to 920 // DSDS use-case. Make sure the the modem stack is enabled. 921 if (!hasActiveOpptProfile) mTelephonyManager.enableModemForSlot(i, true); 922 } 923 } 924 925 @VisibleForTesting init(Context c, ONSProfileSelectionCallback profileSelectionCallback)926 protected void init(Context c, ONSProfileSelectionCallback profileSelectionCallback) { 927 mContext = c; 928 mSequenceId = START_SEQUENCE_ID; 929 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 930 mProfileSelectionCallback = profileSelectionCallback; 931 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 932 mSubscriptionBoundTelephonyManager = mTelephonyManager.createForSubscriptionId( 933 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 934 mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); 935 mNetworkScanCtlr = new ONSNetworkScanCtlr(mContext, mSubscriptionBoundTelephonyManager, 936 mNetworkAvailableCallBack); 937 mEuiccManager = c.getSystemService(EuiccManager.class); 938 updateOpportunisticSubscriptions(); 939 mThread = new HandlerThread(LOG_TAG); 940 mThread.start(); 941 mHandler = new Handler(mThread.getLooper()) { 942 @Override 943 public void handleMessage(Message msg) { 944 switch (msg.what) { 945 case MSG_PROFILE_UPDATE: 946 synchronized (mLock) { 947 updateOpportunisticSubscriptions(); 948 enableModemStackForNonOpportunisticSlots(); 949 } 950 break; 951 case MSG_START_PROFILE_SELECTION: 952 logDebug("Msg received for profile update"); 953 synchronized (mLock) { 954 checkProfileUpdate((Object[]) msg.obj); 955 } 956 break; 957 case MSG_STOP_PROFILE_SELECTION: 958 logDebug("Msg received to stop profile selection"); 959 synchronized (mLock) { 960 stopProfileSelectionProcess((IUpdateAvailableNetworksCallback) msg.obj); 961 } 962 break; 963 case MSG_SUB_SWITCH_COMPLETE: 964 logDebug("Msg received for sub switch"); 965 synchronized (mLock) { 966 onSubSwitchComplete((int) msg.obj); 967 } 968 break; 969 default: 970 log("invalid message"); 971 break; 972 } 973 } 974 }; 975 /* register for profile update events */ 976 mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener( 977 AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener); 978 } 979 log(String msg)980 private void log(String msg) { 981 Rlog.d(LOG_TAG, msg); 982 } 983 logDebug(String msg)984 private void logDebug(String msg) { 985 if (DBG) { 986 Rlog.d(LOG_TAG, msg); 987 } 988 } 989 } 990