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