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