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