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.SignalStrength; 35 import android.telephony.SubscriptionInfo; 36 import android.telephony.SubscriptionManager; 37 import android.telephony.TelephonyFrameworkInitializer; 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 OpportunisticNetworkService 152 .CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 153 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 154 TelephonyManager 155 .UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED); 156 } else { 157 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 158 TelephonyManager 159 .UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); 160 } 161 } 162 mNetworkScanCallback = null; 163 } 164 } 165 } 166 } 167 168 private void handleNetworkScanResult(int subId) { 169 /* if subscription is already active, just enable modem */ 170 if (mSubscriptionManager.isActiveSubId(subId)) { 171 if (enableModem(subId, true)) { 172 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 173 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 174 } else { 175 if (Compatibility.isChangeEnabled( 176 OpportunisticNetworkService 177 .CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 178 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 179 TelephonyManager 180 .UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 181 } else { 182 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 183 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 184 } 185 } 186 mProfileSelectionCallback.onProfileSelectionDone(); 187 synchronized (mLock) { 188 mNetworkScanCallback = null; 189 mAvailableNetworkInfos = null; 190 } 191 } else { 192 logDebug("switch to sub:" + subId); 193 switchToSubscription(subId); 194 } 195 } 196 }; 197 198 @VisibleForTesting 199 protected SubscriptionManager.OnOpportunisticSubscriptionsChangedListener 200 mProfileChangeListener = 201 new SubscriptionManager.OnOpportunisticSubscriptionsChangedListener() { 202 @Override 203 public void onOpportunisticSubscriptionsChanged() { 204 logDebug("onOpportunisticSubscriptionsChanged."); 205 mHandler.sendEmptyMessage(MSG_PROFILE_UPDATE); 206 } 207 }; 208 209 /** 210 * interface call back to confirm profile selection 211 */ 212 public interface ONSProfileSelectionCallback { 213 214 /** 215 * interface call back to confirm profile selection 216 */ onProfileSelectionDone()217 void onProfileSelectionDone(); 218 } 219 220 class SortSubInfo implements Comparator<SubscriptionInfo> 221 { 222 // Used for sorting in ascending order of sub id compare(SubscriptionInfo a, SubscriptionInfo b)223 public int compare(SubscriptionInfo a, SubscriptionInfo b) 224 { 225 return a.getSubscriptionId() - b.getSubscriptionId(); 226 } 227 } 228 229 class SortAvailableNetworks implements Comparator<AvailableNetworkInfo> 230 { 231 // Used for sorting in ascending order of sub id compare(AvailableNetworkInfo a, AvailableNetworkInfo b)232 public int compare(AvailableNetworkInfo a, AvailableNetworkInfo b) 233 { 234 return a.getSubId() - b.getSubId(); 235 } 236 } 237 238 class SortAvailableNetworksInPriority implements Comparator<AvailableNetworkInfo> 239 { 240 // Used for sorting in descending order of priority (ascending order of priority numbers) compare(AvailableNetworkInfo a, AvailableNetworkInfo b)241 public int compare(AvailableNetworkInfo a, AvailableNetworkInfo b) 242 { 243 return a.getPriority() - b.getPriority(); 244 } 245 } 246 247 /** 248 * ONSProfileSelector constructor 249 * @param c context 250 * @param profileSelectionCallback callback to be called once selection is done 251 */ ONSProfileSelector(Context c, ONSProfileSelectionCallback profileSelectionCallback)252 public ONSProfileSelector(Context c, ONSProfileSelectionCallback profileSelectionCallback) { 253 init(c, profileSelectionCallback); 254 log("ONSProfileSelector init complete"); 255 } 256 getSignalLevel(CellInfo cellInfo)257 private int getSignalLevel(CellInfo cellInfo) { 258 if (cellInfo != null) { 259 return cellInfo.getCellSignalStrength().getLevel(); 260 } else { 261 return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 262 } 263 } 264 getMcc(CellInfo cellInfo)265 private String getMcc(CellInfo cellInfo) { 266 String mcc = ""; 267 if (cellInfo instanceof CellInfoLte) { 268 mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString(); 269 } 270 271 return mcc; 272 } 273 getMnc(CellInfo cellInfo)274 private String getMnc(CellInfo cellInfo) { 275 String mnc = ""; 276 if (cellInfo instanceof CellInfoLte) { 277 mnc = ((CellInfoLte) cellInfo).getCellIdentity().getMncString(); 278 } 279 280 return mnc; 281 } 282 getSubIdUsingAvailableNetworks(String mcc, String mnc, int priorityLevel)283 private int getSubIdUsingAvailableNetworks(String mcc, String mnc, int priorityLevel) { 284 String mccMnc = mcc + mnc; 285 synchronized (mLock) { 286 if (mAvailableNetworkInfos != null) { 287 for (AvailableNetworkInfo availableNetworkInfo : mAvailableNetworkInfos) { 288 if (availableNetworkInfo.getPriority() != priorityLevel) { 289 continue; 290 } 291 for (String availableMccMnc : availableNetworkInfo.getMccMncs()) { 292 if (TextUtils.equals(availableMccMnc, mccMnc)) { 293 return availableNetworkInfo.getSubId(); 294 } 295 } 296 } 297 } 298 } 299 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 300 } 301 getOpprotunisticSubInfo(int subId)302 public SubscriptionInfo getOpprotunisticSubInfo(int subId) { 303 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 304 return null; 305 } 306 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 307 if (subscriptionInfo.getSubscriptionId() == subId) { 308 return subscriptionInfo; 309 } 310 } 311 return null; 312 } 313 isOpprotunisticSub(int subId)314 public boolean isOpprotunisticSub(int subId) { 315 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 316 return false; 317 } 318 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 319 if (subscriptionInfo.getSubscriptionId() == subId) { 320 return true; 321 } 322 } 323 return false; 324 } 325 hasOpprotunisticSub(List<AvailableNetworkInfo> availableNetworks)326 public boolean hasOpprotunisticSub(List<AvailableNetworkInfo> availableNetworks) { 327 if ((availableNetworks == null) || (availableNetworks.size() == 0)) { 328 return false; 329 } 330 if ((mOppSubscriptionInfos == null) || (mOppSubscriptionInfos.size() == 0)) { 331 return false; 332 } 333 334 for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) { 335 if (!isOpprotunisticSub(availableNetworkInfo.getSubId())) { 336 return false; 337 } 338 } 339 return true; 340 } 341 isAvtiveSub(int subId)342 private boolean isAvtiveSub(int subId) { 343 return mSubscriptionManager.isActiveSubscriptionId(subId); 344 } 345 346 private HashMap<Integer, IUpdateAvailableNetworksCallback> callbackStubs = new HashMap<>(); 347 switchToSubscription(int subId)348 private void switchToSubscription(int subId) { 349 Intent callbackIntent = new Intent(ACTION_SUB_SWITCH); 350 callbackIntent.setClass(mContext, OpportunisticNetworkService.class); 351 updateToken(); 352 callbackIntent.putExtra("sequenceId", mSequenceId); 353 callbackIntent.putExtra("subId", subId); 354 mSubId = subId; 355 PendingIntent replyIntent = PendingIntent.getService(mContext, 356 1, callbackIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); 357 mSubscriptionManager.switchToSubscription(subId, replyIntent); 358 } 359 onSubSwitchComplete(Intent intent)360 void onSubSwitchComplete(Intent intent) { 361 int sequenceId = intent.getIntExtra("sequenceId", INVALID_SEQUENCE_ID); 362 int subId = intent.getIntExtra("subId", 363 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 364 logDebug("ACTION_SUB_SWITCH sequenceId: " + sequenceId 365 + " mSequenceId: " + mSequenceId 366 + " mSubId: " + mSubId 367 + " subId: " + subId); 368 Message message = Message.obtain(mHandler, MSG_SUB_SWITCH_COMPLETE, subId); 369 message.sendToTarget(); 370 } 371 onSubSwitchComplete(int subId)372 private void onSubSwitchComplete(int subId) { 373 /* Ignore if this is callback for an older request */ 374 if (mSubId != subId) { 375 return; 376 } 377 378 if (enableModem(subId, true)) { 379 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 380 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 381 } else { 382 if (Compatibility.isChangeEnabled( 383 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 384 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 385 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 386 } else { 387 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 388 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 389 } 390 } 391 mProfileSelectionCallback.onProfileSelectionDone(); 392 mNetworkScanCallback = null; 393 mAvailableNetworkInfos = null; 394 } 395 updateToken()396 private void updateToken() { 397 synchronized (mLock) { 398 mSequenceId++; 399 } 400 } 401 getFilteredAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, List<SubscriptionInfo> subscriptionInfoList)402 private ArrayList<AvailableNetworkInfo> getFilteredAvailableNetworks( 403 ArrayList<AvailableNetworkInfo> availableNetworks, 404 List<SubscriptionInfo> subscriptionInfoList) { 405 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 406 new ArrayList<AvailableNetworkInfo>(); 407 408 /* instead of checking each element of a list every element of the other, sort them in 409 the order of sub id and compare to improve the filtering performance. */ 410 Collections.sort(subscriptionInfoList, new SortSubInfo()); 411 Collections.sort(availableNetworks, new SortAvailableNetworks()); 412 int availableNetworksIndex = 0; 413 int subscriptionInfoListIndex = 0; 414 SubscriptionInfo subscriptionInfo; 415 AvailableNetworkInfo availableNetwork; 416 417 while (availableNetworksIndex < availableNetworks.size() 418 && subscriptionInfoListIndex < subscriptionInfoList.size()) { 419 subscriptionInfo = subscriptionInfoList.get(subscriptionInfoListIndex); 420 availableNetwork = availableNetworks.get(availableNetworksIndex); 421 if (subscriptionInfo.getSubscriptionId() == availableNetwork.getSubId()) { 422 filteredAvailableNetworks.add(availableNetwork); 423 subscriptionInfoListIndex++; 424 availableNetworksIndex++; 425 } else if (subscriptionInfo.getSubscriptionId() < availableNetwork.getSubId()) { 426 subscriptionInfoListIndex++; 427 } else { 428 availableNetworksIndex++; 429 } 430 } 431 return filteredAvailableNetworks; 432 } 433 isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, ArrayList<AvailableNetworkInfo> availableNetworks2)434 private boolean isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, 435 ArrayList<AvailableNetworkInfo> availableNetworks2) { 436 if ((availableNetworks1 == null) || (availableNetworks2 == null)) { 437 return false; 438 } 439 return new HashSet<>(availableNetworks1).equals(new HashSet<>(availableNetworks2)); 440 } 441 isPrimaryActiveOnOpportunisticSlot( ArrayList<AvailableNetworkInfo> availableNetworks)442 private boolean isPrimaryActiveOnOpportunisticSlot( 443 ArrayList<AvailableNetworkInfo> availableNetworks) { 444 /* Check if any of the available network is an embedded profile. if none are embedded, 445 * return false 446 * Todo <b/130535071> */ 447 if (!isOpportunisticSubEmbedded(availableNetworks)) { 448 return false; 449 } 450 451 List<SubscriptionInfo> subscriptionInfos = 452 mSubscriptionManager.getActiveSubscriptionInfoList(false); 453 if (subscriptionInfos == null) { 454 return false; 455 } 456 457 /* if there is a primary subscription active on the eSIM, return true */ 458 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 459 if (!subscriptionInfo.isOpportunistic() && subscriptionInfo.isEmbedded()) { 460 return true; 461 } 462 } 463 464 return false; 465 466 } sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result)467 private void sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, 468 int result) { 469 if (callback == null) { 470 log("callback is null"); 471 return; 472 } 473 try { 474 callback.onComplete(result); 475 } catch (RemoteException exception) { 476 log("RemoteException " + exception); 477 } 478 } 479 checkProfileUpdate(Object[] objects)480 private void checkProfileUpdate(Object[] objects) { 481 ArrayList<AvailableNetworkInfo> availableNetworks = 482 (ArrayList<AvailableNetworkInfo>) objects[0]; 483 IUpdateAvailableNetworksCallback callbackStub = 484 (IUpdateAvailableNetworksCallback) objects[1]; 485 if (mOppSubscriptionInfos == null) { 486 logDebug("null subscription infos"); 487 if (Compatibility.isChangeEnabled( 488 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 489 sendUpdateNetworksCallbackHelper(callbackStub, 490 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); 491 } else { 492 sendUpdateNetworksCallbackHelper(callbackStub, 493 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 494 } 495 return; 496 } 497 498 /* if primary subscription is active on opportunistic slot, do not switch out the same. */ 499 if (isPrimaryActiveOnOpportunisticSlot(availableNetworks)) { 500 logDebug("primary subscription active on opportunistic sub"); 501 sendUpdateNetworksCallbackHelper(callbackStub, 502 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 503 return; 504 } 505 506 if (isSame(availableNetworks, mAvailableNetworkInfos)) { 507 logDebug("received duplicate requests"); 508 /* If we receive same request more than once, send abort response for earlier one 509 and send actual response for the latest callback. 510 */ 511 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 512 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 513 mNetworkScanCallback = callbackStub; 514 return; 515 } 516 517 stopProfileScanningPrecedure(); 518 mIsEnabled = true; 519 mAvailableNetworkInfos = availableNetworks; 520 /* sort in the order of priority */ 521 Collections.sort(mAvailableNetworkInfos, new SortAvailableNetworksInPriority()); 522 logDebug("availableNetworks: " + availableNetworks); 523 524 if (mOppSubscriptionInfos.size() > 0) { 525 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 526 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 527 getFilteredAvailableNetworks((ArrayList<AvailableNetworkInfo>)availableNetworks, 528 mOppSubscriptionInfos); 529 if ((filteredAvailableNetworks.size() == 1) 530 && ((filteredAvailableNetworks.get(0).getMccMncs() == null) 531 || (filteredAvailableNetworks.get(0).getMccMncs().size() == 0))) { 532 /* if subscription is not active, activate the sub */ 533 if (!mSubscriptionManager.isActiveSubId(filteredAvailableNetworks.get(0).getSubId())) { 534 mNetworkScanCallback = callbackStub; 535 switchToSubscription(filteredAvailableNetworks.get(0).getSubId()); 536 } else { 537 if (enableModem(filteredAvailableNetworks.get(0).getSubId(), true)) { 538 sendUpdateNetworksCallbackHelper(callbackStub, 539 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); 540 } else { 541 if (Compatibility.isChangeEnabled( 542 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 543 sendUpdateNetworksCallbackHelper(callbackStub, 544 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL); 545 } else { 546 sendUpdateNetworksCallbackHelper(callbackStub, 547 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 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 OpportunisticNetworkService.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 OpportunisticNetworkService.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 OpportunisticNetworkService.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 SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 676 if (info == null) { 677 // Subscription is not active. Do nothing. 678 return false; 679 } 680 681 // If disabling modem for opportunistic sub, make sure to switch data back to default sub. 682 if (!enable) { 683 if (mSubscriptionManager.getPreferredDataSubscriptionId() == subId) { 684 selectProfileForData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null); 685 } 686 } 687 int phoneId = info.getSimSlotIndex(); 688 /* Todo: b/135067156 689 * Reenable this code once 135067156 is fixed 690 if (mSubscriptionBoundTelephonyManager.isModemEnabledForSlot(phoneId) == enable) { 691 logDebug("modem is already enabled "); 692 return true; 693 } */ 694 695 return mSubscriptionBoundTelephonyManager.enableModemForSlot(phoneId, enable); 696 } 697 stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub)698 private void stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub) { 699 stopProfileScanningPrecedure(); 700 logDebug("stopProfileSelection"); 701 disableOpportunisticModem(callbackStub); 702 } 703 stopProfileScanningPrecedure()704 private void stopProfileScanningPrecedure() { 705 synchronized (mLock) { 706 if (mNetworkScanCallback != null) { 707 sendUpdateNetworksCallbackHelper(mNetworkScanCallback, 708 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED); 709 mNetworkScanCallback = null; 710 } 711 mNetworkScanCtlr.stopNetworkScan(); 712 713 mAvailableNetworkInfos = null; 714 mIsEnabled = false; 715 } 716 } 717 containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks)718 public boolean containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 719 if (mOppSubscriptionInfos == null) { 720 logDebug("received null subscription infos"); 721 return false; 722 } 723 724 if (mOppSubscriptionInfos.size() > 0) { 725 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 726 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 727 getFilteredAvailableNetworks( 728 (ArrayList<AvailableNetworkInfo>)availableNetworks, mOppSubscriptionInfos); 729 if (filteredAvailableNetworks.size() > 0) { 730 return true; 731 } 732 } 733 734 return false; 735 } 736 containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks)737 public boolean containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks) { 738 if (mStandaloneOppSubInfos == null) { 739 logDebug("received null subscription infos"); 740 return false; 741 } 742 if (mStandaloneOppSubInfos.size() > 0) { 743 logDebug("Standalone opportunistic subInfos size " + mStandaloneOppSubInfos.size()); 744 ArrayList<AvailableNetworkInfo> filteredAvailableNetworks = 745 getFilteredAvailableNetworks( 746 (ArrayList<AvailableNetworkInfo>) availableNetworks, 747 mStandaloneOppSubInfos); 748 if (filteredAvailableNetworks.size() > 0) { 749 return true; 750 } 751 } 752 return false; 753 } 754 isOpportunisticSubActive()755 public boolean isOpportunisticSubActive() { 756 if (mOppSubscriptionInfos == null) { 757 logDebug("received null subscription infos"); 758 return false; 759 } 760 761 if (mOppSubscriptionInfos.size() > 0) { 762 logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size()); 763 for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) { 764 if (mSubscriptionManager.isActiveSubId(subscriptionInfo.getSubscriptionId())) { 765 return true; 766 } 767 } 768 } 769 return false; 770 } 771 startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub)772 public void startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, 773 IUpdateAvailableNetworksCallback callbackStub) { 774 logDebug("startProfileSelection availableNetworks: " + availableNetworks); 775 if (availableNetworks == null || availableNetworks.size() == 0) { 776 if (callbackStub != null) { 777 sendUpdateNetworksCallbackHelper(callbackStub, 778 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); 779 } 780 return; 781 } 782 Object[] objects = new Object[]{availableNetworks, callbackStub}; 783 Message message = Message.obtain(mHandler, MSG_START_PROFILE_SELECTION, objects); 784 message.sendToTarget(); 785 } 786 sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result)787 private void sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result) { 788 if (callback == null) return; 789 try { 790 callback.onComplete(result); 791 } catch (RemoteException exception) { 792 log("RemoteException " + exception); 793 } 794 } 795 796 /** 797 * select opportunistic profile for data if passing a valid subId. 798 * @param subId : opportunistic subId or SubscriptionManager.DEFAULT_SUBSCRIPTION_ID if 799 * deselecting previously set preference. 800 */ selectProfileForData(int subId, boolean needValidation, ISetOpportunisticDataCallback callbackStub)801 public void selectProfileForData(int subId, boolean needValidation, 802 ISetOpportunisticDataCallback callbackStub) { 803 if ((subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) 804 || (isOpprotunisticSub(subId) && mSubscriptionManager.isActiveSubId(subId))) { 805 ISub iSub = ISub.Stub.asInterface( 806 TelephonyFrameworkInitializer 807 .getTelephonyServiceManager() 808 .getSubscriptionServiceRegisterer() 809 .get()); 810 if (iSub == null) { 811 log("Could not get Subscription Service handle"); 812 if (Compatibility.isChangeEnabled( 813 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 814 sendSetOpptCallbackHelper(callbackStub, 815 TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); 816 } else { 817 sendSetOpptCallbackHelper(callbackStub, 818 TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); 819 } 820 return; 821 } 822 try { 823 iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub); 824 } catch (RemoteException ex) { 825 log("Could not connect to Subscription Service"); 826 if (Compatibility.isChangeEnabled( 827 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 828 sendSetOpptCallbackHelper(callbackStub, 829 TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); 830 } else { 831 sendSetOpptCallbackHelper(callbackStub, 832 TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); 833 } 834 return; 835 } 836 mCurrentDataSubId = subId; 837 } else { 838 log("Inactive sub passed for preferred data " + subId); 839 if (Compatibility.isChangeEnabled( 840 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { 841 if (isOpprotunisticSub(subId)) { 842 sendSetOpptCallbackHelper(callbackStub, 843 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 844 } else { 845 sendSetOpptCallbackHelper(callbackStub, 846 TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE); 847 } 848 } else { 849 sendSetOpptCallbackHelper(callbackStub, 850 TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); 851 } 852 } 853 } 854 getPreferredDataSubscriptionId()855 public int getPreferredDataSubscriptionId() { 856 return mSubscriptionManager.getPreferredDataSubscriptionId(); 857 } 858 859 /** 860 * stop profile selection procedure 861 */ stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub)862 public void stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub) { 863 logDebug("stopProfileSelection"); 864 Message message = Message.obtain(mHandler, MSG_STOP_PROFILE_SELECTION, callbackStub); 865 message.sendToTarget(); 866 } 867 868 @VisibleForTesting updateOpportunisticSubscriptions()869 protected void updateOpportunisticSubscriptions() { 870 synchronized (mLock) { 871 mOppSubscriptionInfos = mSubscriptionManager 872 .getOpportunisticSubscriptions().stream() 873 .filter(subInfo -> subInfo.isGroupDisabled() != true) 874 .collect(Collectors.toList()); 875 if (mOppSubscriptionInfos != null) { 876 mStandaloneOppSubInfos = mOppSubscriptionInfos.stream() 877 .filter(subInfo -> subInfo.getGroupUuid() == null) 878 .collect(Collectors.toList()); 879 } 880 } 881 } 882 enableModemStackForNonOpportunisticSlots()883 private void enableModemStackForNonOpportunisticSlots() { 884 int phoneCount = mTelephonyManager.getPhoneCount(); 885 // Do nothing in single SIM mode. 886 if (phoneCount < 2) return; 887 888 for (int i = 0; i < phoneCount; i++) { 889 boolean hasActiveOpptProfile = false; 890 for (SubscriptionInfo info : mOppSubscriptionInfos) { 891 if (info.getSimSlotIndex() == i) { 892 hasActiveOpptProfile = true; 893 } 894 } 895 // If the slot doesn't have active opportunistic profile anymore, it's back to 896 // DSDS use-case. Make sure the the modem stack is enabled. 897 if (!hasActiveOpptProfile) mTelephonyManager.enableModemForSlot(i, true); 898 } 899 } 900 901 @VisibleForTesting init(Context c, ONSProfileSelectionCallback profileSelectionCallback)902 protected void init(Context c, ONSProfileSelectionCallback profileSelectionCallback) { 903 mContext = c; 904 mSequenceId = START_SEQUENCE_ID; 905 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 906 mProfileSelectionCallback = profileSelectionCallback; 907 mTelephonyManager = (TelephonyManager) 908 mContext.getSystemService(Context.TELEPHONY_SERVICE); 909 mSubscriptionBoundTelephonyManager = mTelephonyManager.createForSubscriptionId( 910 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 911 mSubscriptionManager = (SubscriptionManager) 912 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 913 mNetworkScanCtlr = new ONSNetworkScanCtlr(mContext, mSubscriptionBoundTelephonyManager, 914 mNetworkAvailableCallBack); 915 updateOpportunisticSubscriptions(); 916 mThread = new HandlerThread(LOG_TAG); 917 mThread.start(); 918 mHandler = new Handler(mThread.getLooper()) { 919 @Override 920 public void handleMessage(Message msg) { 921 switch (msg.what) { 922 case MSG_PROFILE_UPDATE: 923 synchronized (mLock) { 924 updateOpportunisticSubscriptions(); 925 enableModemStackForNonOpportunisticSlots(); 926 } 927 break; 928 case MSG_START_PROFILE_SELECTION: 929 logDebug("Msg received for profile update"); 930 synchronized (mLock) { 931 checkProfileUpdate((Object[]) msg.obj); 932 } 933 break; 934 case MSG_STOP_PROFILE_SELECTION: 935 logDebug("Msg received to stop profile selection"); 936 synchronized (mLock) { 937 stopProfileSelectionProcess((IUpdateAvailableNetworksCallback) msg.obj); 938 } 939 break; 940 case MSG_SUB_SWITCH_COMPLETE: 941 logDebug("Msg received for sub switch"); 942 synchronized (mLock) { 943 onSubSwitchComplete((int) msg.obj); 944 } 945 break; 946 default: 947 log("invalid message"); 948 break; 949 } 950 } 951 }; 952 /* register for profile update events */ 953 mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener( 954 AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener); 955 } 956 log(String msg)957 private void log(String msg) { 958 Rlog.d(LOG_TAG, msg); 959 } 960 logDebug(String msg)961 private void logDebug(String msg) { 962 if (DBG) { 963 Rlog.d(LOG_TAG, msg); 964 } 965 } 966 } 967