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