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