• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         mEuiccManager.switchToSubscription(subId, availableSIMPortIndex, replyIntent);
375     }
376 
377     @VisibleForTesting
getAvailableESIMPortIndex()378     protected int getAvailableESIMPortIndex() {
379         //Check if an opportunistic subscription is already active. If yes then, use the same port.
380         List<SubscriptionInfo> subscriptionInfos = mSubscriptionManager
381                 .getCompleteActiveSubscriptionInfoList();
382         if (subscriptionInfos != null) {
383             logDebug("[getAvailableESIMPortIndex] subscriptionInfos size:"
384                     + subscriptionInfos.size());
385             for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
386                 if (subscriptionInfo.isEmbedded() && subscriptionInfo.isOpportunistic()) {
387                     return subscriptionInfo.getPortIndex();
388                 }
389             }
390         }
391 
392         //Look for available port.
393         for (UiccCardInfo uiccCardInfo : mTelephonyManager.getUiccCardsInfo()) {
394             logDebug("[getAvailableESIMPortIndex] CardInfo: " + uiccCardInfo.toString());
395             if (!uiccCardInfo.isEuicc()) {
396                 continue;
397             }
398 
399             EuiccManager euiccManager = mEuiccManager.createForCardId(uiccCardInfo.getCardId());
400             for (UiccPortInfo uiccPortInfo : uiccCardInfo.getPorts()) {
401                 logDebug("[getAvailableESIMPortIndex] PortInfo: " + uiccPortInfo.toString());
402                 //Port is available if no profiles enabled on it.
403                 if (euiccManager.isSimPortAvailable(uiccPortInfo.getPortIndex())) {
404                     return uiccPortInfo.getPortIndex();
405                 }
406             }
407         }
408 
409         logDebug("[getAvailableESIMPortIndex] No Port is available.");
410         return TelephonyManager.INVALID_PORT_INDEX;
411     }
412 
onSubSwitchComplete(Intent intent)413     void onSubSwitchComplete(Intent intent) {
414         int sequenceId = intent.getIntExtra("sequenceId",  INVALID_SEQUENCE_ID);
415         int subId = intent.getIntExtra("subId",
416                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
417         logDebug("ACTION_SUB_SWITCH sequenceId: " + sequenceId
418                 + " mSequenceId: " + mSequenceId
419                 + " mSubId: " + mSubId
420                 + " subId: " + subId);
421         Message message = Message.obtain(mHandler, MSG_SUB_SWITCH_COMPLETE, subId);
422         message.sendToTarget();
423     }
424 
onSubSwitchComplete(int subId)425     private void onSubSwitchComplete(int subId) {
426         /* Ignore if this is callback for an older request */
427         if (mSubId != subId) {
428             return;
429         }
430 
431         if (enableModem(subId, true)) {
432             sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
433                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
434         } else {
435             if (Compatibility.isChangeEnabled(
436                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
437                 sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
438                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL);
439             } else {
440                 sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
441                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
442             }
443         }
444         mProfileSelectionCallback.onProfileSelectionDone();
445         mNetworkScanCallback = null;
446         mAvailableNetworkInfos = null;
447     }
448 
updateToken()449     private void updateToken() {
450         synchronized (mLock) {
451             mSequenceId++;
452         }
453     }
454 
getFilteredAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, List<SubscriptionInfo> subscriptionInfoList)455     private ArrayList<AvailableNetworkInfo> getFilteredAvailableNetworks(
456             ArrayList<AvailableNetworkInfo> availableNetworks,
457             List<SubscriptionInfo> subscriptionInfoList) {
458         ArrayList<AvailableNetworkInfo> filteredAvailableNetworks =
459                 new ArrayList<AvailableNetworkInfo>();
460 
461         /* instead of checking each element of a list every element of the other, sort them in
462            the order of sub id and compare to improve the filtering performance. */
463         Collections.sort(subscriptionInfoList, new SortSubInfo());
464         Collections.sort(availableNetworks, new SortAvailableNetworks());
465         int availableNetworksIndex = 0;
466         int subscriptionInfoListIndex = 0;
467         SubscriptionInfo subscriptionInfo;
468         AvailableNetworkInfo availableNetwork;
469 
470         while (availableNetworksIndex < availableNetworks.size()
471                 && subscriptionInfoListIndex < subscriptionInfoList.size()) {
472             subscriptionInfo = subscriptionInfoList.get(subscriptionInfoListIndex);
473             availableNetwork = availableNetworks.get(availableNetworksIndex);
474             if (subscriptionInfo.getSubscriptionId() == availableNetwork.getSubId()) {
475                 filteredAvailableNetworks.add(availableNetwork);
476                 subscriptionInfoListIndex++;
477                 availableNetworksIndex++;
478             } else if (subscriptionInfo.getSubscriptionId() < availableNetwork.getSubId()) {
479                 subscriptionInfoListIndex++;
480             } else {
481                 availableNetworksIndex++;
482             }
483         }
484         return filteredAvailableNetworks;
485     }
486 
isSame(ArrayList<AvailableNetworkInfo> availableNetworks1, ArrayList<AvailableNetworkInfo> availableNetworks2)487     private boolean isSame(ArrayList<AvailableNetworkInfo> availableNetworks1,
488             ArrayList<AvailableNetworkInfo> availableNetworks2) {
489         if ((availableNetworks1 == null) || (availableNetworks2 == null)) {
490             return false;
491         }
492         return new HashSet<>(availableNetworks1).equals(new HashSet<>(availableNetworks2));
493     }
494 
sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result)495     private void sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback,
496             int result) {
497         if (callback == null) {
498             log("callback is null");
499             return;
500         }
501         try {
502             callback.onComplete(result);
503         } catch (RemoteException exception) {
504             log("RemoteException " + exception);
505         }
506     }
507 
checkProfileUpdate(Object[] objects)508     private void checkProfileUpdate(Object[] objects) {
509         ArrayList<AvailableNetworkInfo> availableNetworks =
510                 (ArrayList<AvailableNetworkInfo>) objects[0];
511         IUpdateAvailableNetworksCallback callbackStub =
512                 (IUpdateAvailableNetworksCallback) objects[1];
513         if (mOppSubscriptionInfos == null) {
514             logDebug("null subscription infos");
515             if (Compatibility.isChangeEnabled(
516                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
517                 sendUpdateNetworksCallbackHelper(callbackStub,
518                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
519             } else {
520                 sendUpdateNetworksCallbackHelper(callbackStub,
521                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
522             }
523             return;
524         }
525 
526         /* Check if ports are available on the embedded slot */
527         int availSIMPortIndex = getAvailableESIMPortIndex();
528         if (availSIMPortIndex == TelephonyManager.INVALID_PORT_INDEX) {
529             logDebug("SIM port not available.");
530             sendUpdateNetworksCallbackHelper(callbackStub,
531                     TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE);
532             return;
533         }
534 
535         if (isSame(availableNetworks, mAvailableNetworkInfos)) {
536             logDebug("received duplicate requests");
537             /* If we receive same request more than once, send abort response for earlier one
538                and send actual response for the latest callback.
539             */
540             sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
541                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
542             mNetworkScanCallback = callbackStub;
543             return;
544         }
545 
546         stopProfileScanningPrecedure();
547         mIsEnabled = true;
548         mAvailableNetworkInfos = availableNetworks;
549         /* sort in the order of priority */
550         Collections.sort(mAvailableNetworkInfos, new SortAvailableNetworksInPriority());
551         logDebug("availableNetworks: " + availableNetworks);
552 
553         if (mOppSubscriptionInfos.size() > 0) {
554             logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size());
555             ArrayList<AvailableNetworkInfo> filteredAvailableNetworks =
556                     getFilteredAvailableNetworks((ArrayList<AvailableNetworkInfo>)availableNetworks,
557                             mOppSubscriptionInfos);
558             if ((filteredAvailableNetworks.size() == 1)
559                     && ((filteredAvailableNetworks.get(0).getMccMncs() == null)
560                     || (filteredAvailableNetworks.get(0).getMccMncs().size() == 0))) {
561                 /* if subscription is not active, activate the sub */
562                 if (!mSubscriptionManager.isActiveSubId(filteredAvailableNetworks.get(0).getSubId())) {
563                     mNetworkScanCallback = callbackStub;
564                     switchToSubscription(filteredAvailableNetworks.get(0).getSubId(),
565                             availSIMPortIndex);
566                 } else {
567                     if (enableModem(filteredAvailableNetworks.get(0).getSubId(), true)) {
568                         sendUpdateNetworksCallbackHelper(callbackStub,
569                             TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
570                     } else {
571                         if (Compatibility.isChangeEnabled(
572                                 OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
573                             sendUpdateNetworksCallbackHelper(callbackStub,
574                                     TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL);
575                         } else {
576                             sendUpdateNetworksCallbackHelper(callbackStub,
577                                     TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
578                         }
579                     }
580                     mProfileSelectionCallback.onProfileSelectionDone();
581                     mAvailableNetworkInfos = null;
582                 }
583             } else {
584                 mNetworkScanCallback = callbackStub;
585                 /* start scan immediately */
586                 mNetworkScanCtlr.startFastNetworkScan(filteredAvailableNetworks);
587             }
588         } else if (mOppSubscriptionInfos.size() == 0) {
589             if (Compatibility.isChangeEnabled(
590                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
591                 sendUpdateNetworksCallbackHelper(callbackStub,
592                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
593             } else {
594                 sendUpdateNetworksCallbackHelper(callbackStub,
595                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
596             }
597             /* check if no profile */
598             logDebug("stopping scan");
599             mNetworkScanCtlr.stopNetworkScan();
600         }
601     }
602 
isActiveSub(int subId)603     private boolean isActiveSub(int subId) {
604         List<SubscriptionInfo> subscriptionInfos =
605                 mSubscriptionManager.getActiveSubscriptionInfoList(false);
606         if (subscriptionInfos == null) {
607             return false;
608         }
609 
610         for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
611             if (subscriptionInfo.getSubscriptionId() == subId) {
612                 return true;
613             }
614         }
615 
616         return false;
617     }
618 
619     @VisibleForTesting
retrieveBestSubscription(List<CellInfo> results)620     protected int retrieveBestSubscription(List<CellInfo> results) {
621         /* sort the results according to signal strength level */
622         Collections.sort(results, new Comparator<CellInfo>() {
623             @Override
624             public int compare(CellInfo cellInfo1, CellInfo cellInfo2) {
625                 return getSignalLevel(cellInfo1) - getSignalLevel(cellInfo2);
626             }
627         });
628 
629         for (int level = PRIORITY_HIGH; level < PRIORITY_LOW; level++) {
630             for (CellInfo result : results) {
631                 /* get subscription id for the best network scan result */
632                 int subId = getSubIdUsingAvailableNetworks(getMcc(result), getMnc(result), level);
633                 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
634                     return subId;
635                 }
636             }
637         }
638 
639         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
640     }
641 
isOpportunisticSubEmbedded( ArrayList<AvailableNetworkInfo> availableNetworks)642     private boolean isOpportunisticSubEmbedded(
643             ArrayList<AvailableNetworkInfo> availableNetworks) {
644         List<SubscriptionInfo> subscriptionInfos =
645             mSubscriptionManager.getOpportunisticSubscriptions();
646         if (subscriptionInfos == null) {
647             return false;
648         }
649         for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) {
650             for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
651                 if (subscriptionInfo.getSubscriptionId() == availableNetworkInfo.getSubId()
652                         && subscriptionInfo.isEmbedded()) {
653                     return true;
654                 }
655             }
656         }
657 
658         return false;
659     }
660 
getActiveOpportunisticSubId()661     private int getActiveOpportunisticSubId() {
662         List<SubscriptionInfo> subscriptionInfos =
663             mSubscriptionManager.getActiveSubscriptionInfoList(false);
664         if (subscriptionInfos == null) {
665             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
666         }
667         for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
668             if (subscriptionInfo.isOpportunistic()) {
669                 return subscriptionInfo.getSubscriptionId();
670             }
671         }
672 
673         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
674     }
675 
disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub)676     private void disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub) {
677         int subId = getActiveOpportunisticSubId();
678         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
679             if (Compatibility.isChangeEnabled(
680                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
681                 sendUpdateNetworksCallbackHelper(callbackStub,
682                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
683             } else {
684                 sendUpdateNetworksCallbackHelper(callbackStub,
685                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
686             }
687             return;
688         }
689         if (enableModem(subId, false)) {
690             sendUpdateNetworksCallbackHelper(callbackStub,
691                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
692         } else {
693             if (Compatibility.isChangeEnabled(
694                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
695                 sendUpdateNetworksCallbackHelper(callbackStub,
696                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL);
697             } else {
698                 sendUpdateNetworksCallbackHelper(callbackStub,
699                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
700             }
701         }
702     }
703 
enableModem(int subId, boolean enable)704     private boolean enableModem(int subId, boolean enable) {
705         SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
706         if (info == null) {
707             // Subscription is not active. Do nothing.
708             return false;
709         }
710 
711         // If disabling modem for opportunistic sub, make sure to switch data back to default sub.
712         if (!enable) {
713             if (mSubscriptionManager.getPreferredDataSubscriptionId() == subId) {
714                 selectProfileForData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
715             }
716         }
717         int phoneId = info.getSimSlotIndex();
718         /*  Todo: b/135067156
719          *  Reenable this code once 135067156 is fixed
720         if (mSubscriptionBoundTelephonyManager.isModemEnabledForSlot(phoneId) == enable) {
721             logDebug("modem is already enabled ");
722             return true;
723         } */
724 
725         return mSubscriptionBoundTelephonyManager.enableModemForSlot(phoneId, enable);
726     }
727 
stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub)728     private void stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub) {
729         stopProfileScanningPrecedure();
730         logDebug("stopProfileSelection");
731         disableOpportunisticModem(callbackStub);
732     }
733 
stopProfileScanningPrecedure()734     private void stopProfileScanningPrecedure() {
735         synchronized (mLock) {
736             if (mNetworkScanCallback != null) {
737                 sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
738                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
739                 mNetworkScanCallback = null;
740             }
741             mNetworkScanCtlr.stopNetworkScan();
742 
743             mAvailableNetworkInfos = null;
744             mIsEnabled = false;
745         }
746     }
747 
containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks)748     public boolean containsOpportunisticSubs(ArrayList<AvailableNetworkInfo> availableNetworks) {
749         if (mOppSubscriptionInfos == null) {
750             logDebug("received null subscription infos");
751             return false;
752         }
753 
754         if (mOppSubscriptionInfos.size() > 0) {
755             logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size());
756             ArrayList<AvailableNetworkInfo> filteredAvailableNetworks =
757                     getFilteredAvailableNetworks(
758                             (ArrayList<AvailableNetworkInfo>)availableNetworks, mOppSubscriptionInfos);
759             if (filteredAvailableNetworks.size() > 0) {
760                 return true;
761             }
762         }
763 
764         return false;
765     }
766 
containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks)767     public boolean containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks) {
768         if (mStandaloneOppSubInfos == null) {
769             logDebug("received null subscription infos");
770             return false;
771         }
772         if (mStandaloneOppSubInfos.size() > 0) {
773             logDebug("Standalone opportunistic subInfos size " + mStandaloneOppSubInfos.size());
774             ArrayList<AvailableNetworkInfo> filteredAvailableNetworks =
775                     getFilteredAvailableNetworks(
776                             (ArrayList<AvailableNetworkInfo>) availableNetworks,
777                             mStandaloneOppSubInfos);
778             if (filteredAvailableNetworks.size() > 0) {
779                 return true;
780             }
781         }
782         return false;
783     }
784 
isOpportunisticSubActive()785     public boolean isOpportunisticSubActive() {
786         if (mOppSubscriptionInfos == null) {
787             logDebug("received null subscription infos");
788             return false;
789         }
790 
791         if (mOppSubscriptionInfos.size() > 0) {
792             logDebug("opportunistic subscriptions size " + mOppSubscriptionInfos.size());
793             for (SubscriptionInfo subscriptionInfo : mOppSubscriptionInfos) {
794                 if (mSubscriptionManager.isActiveSubId(subscriptionInfo.getSubscriptionId())) {
795                     return true;
796                 }
797             }
798         }
799         return false;
800     }
801 
startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub)802     public void startProfileSelection(ArrayList<AvailableNetworkInfo> availableNetworks,
803             IUpdateAvailableNetworksCallback callbackStub) {
804         logDebug("startProfileSelection availableNetworks: " + availableNetworks);
805         if (availableNetworks == null || availableNetworks.size() == 0) {
806             if (callbackStub != null) {
807                 sendUpdateNetworksCallbackHelper(callbackStub,
808                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
809             }
810             return;
811         }
812         Object[] objects = new Object[]{availableNetworks, callbackStub};
813         Message message = Message.obtain(mHandler, MSG_START_PROFILE_SELECTION, objects);
814         message.sendToTarget();
815     }
816 
sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result)817     private void sendSetOpptCallbackHelper(ISetOpportunisticDataCallback callback, int result) {
818         if (callback == null) return;
819         try {
820             callback.onComplete(result);
821         } catch (RemoteException exception) {
822             log("RemoteException " + exception);
823         }
824     }
825 
826     /**
827      * select opportunistic profile for data if passing a valid subId.
828      * @param subId : opportunistic subId or SubscriptionManager.DEFAULT_SUBSCRIPTION_ID if
829      *              deselecting previously set preference.
830      */
selectProfileForData(int subId, boolean needValidation, ISetOpportunisticDataCallback callbackStub)831     public void selectProfileForData(int subId, boolean needValidation,
832             ISetOpportunisticDataCallback callbackStub) {
833         if ((subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
834                 || (isOpprotunisticSub(subId) && mSubscriptionManager.isActiveSubId(subId))) {
835             try {
836                 mSubscriptionManager.setPreferredDataSubscriptionId(subId, needValidation,
837                         mHandler::post, result -> sendSetOpptCallbackHelper(callbackStub, result));
838             } catch (Exception ex) {
839                 log("setPreferredDataSubscriptionId failed. subId=" + subId + ", needValidation="
840                         + needValidation + ", ex=" + ex);
841                 if (Compatibility.isChangeEnabled(
842                         OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
843                     sendSetOpptCallbackHelper(callbackStub,
844                             TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
845                 } else {
846                     sendSetOpptCallbackHelper(callbackStub,
847                             TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
848                 }
849                 return;
850             }
851             mCurrentDataSubId = subId;
852         } else {
853             log("Inactive sub passed for preferred data " + subId);
854             if (Compatibility.isChangeEnabled(
855                     OpportunisticNetworkService.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
856                 if (isOpprotunisticSub(subId)) {
857                     sendSetOpptCallbackHelper(callbackStub,
858                             TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
859                 } else {
860                     sendSetOpptCallbackHelper(callbackStub,
861                             TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE);
862                 }
863             } else {
864                 sendSetOpptCallbackHelper(callbackStub,
865                         TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
866             }
867         }
868     }
869 
getPreferredDataSubscriptionId()870     public int getPreferredDataSubscriptionId() {
871         return mSubscriptionManager.getPreferredDataSubscriptionId();
872     }
873 
874     /**
875      * stop profile selection procedure
876      */
stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub)877     public void stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub) {
878         logDebug("stopProfileSelection");
879         Message message = Message.obtain(mHandler, MSG_STOP_PROFILE_SELECTION, callbackStub);
880         message.sendToTarget();
881     }
882 
883     @VisibleForTesting
updateOpportunisticSubscriptions()884     protected void updateOpportunisticSubscriptions() {
885         synchronized (mLock) {
886             mOppSubscriptionInfos = mSubscriptionManager
887                     .getOpportunisticSubscriptions().stream()
888                     .filter(subInfo -> subInfo.isGroupDisabled() != true)
889                     .collect(Collectors.toList());
890             if (mOppSubscriptionInfos != null) {
891                 mStandaloneOppSubInfos = mOppSubscriptionInfos.stream()
892                         .filter(subInfo -> subInfo.getGroupUuid() == null)
893                         .collect(Collectors.toList());
894             }
895         }
896     }
897 
enableModemStackForNonOpportunisticSlots()898     private void enableModemStackForNonOpportunisticSlots() {
899         int phoneCount = mTelephonyManager.getPhoneCount();
900         // Do nothing in single SIM mode.
901         if (phoneCount < 2) return;
902 
903         for (int i = 0; i < phoneCount; i++) {
904             boolean hasActiveOpptProfile = false;
905             for (SubscriptionInfo info : mOppSubscriptionInfos) {
906                 if (info.getSimSlotIndex() == i) {
907                     hasActiveOpptProfile = true;
908                 }
909             }
910             // If the slot doesn't have active opportunistic profile anymore, it's back to
911             // DSDS use-case. Make sure the the modem stack is enabled.
912             if (!hasActiveOpptProfile) mTelephonyManager.enableModemForSlot(i, true);
913         }
914     }
915 
916     @VisibleForTesting
init(Context c, ONSProfileSelectionCallback profileSelectionCallback)917     protected void init(Context c, ONSProfileSelectionCallback profileSelectionCallback) {
918         mContext = c;
919         mSequenceId = START_SEQUENCE_ID;
920         mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
921         mProfileSelectionCallback = profileSelectionCallback;
922         mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
923         mSubscriptionBoundTelephonyManager = mTelephonyManager.createForSubscriptionId(
924                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
925         mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
926         mNetworkScanCtlr = new ONSNetworkScanCtlr(mContext, mSubscriptionBoundTelephonyManager,
927                 mNetworkAvailableCallBack);
928         mEuiccManager = c.getSystemService(EuiccManager.class);
929         updateOpportunisticSubscriptions();
930         mThread = new HandlerThread(LOG_TAG);
931         mThread.start();
932         mHandler = new Handler(mThread.getLooper()) {
933             @Override
934             public void handleMessage(Message msg) {
935                 switch (msg.what) {
936                     case MSG_PROFILE_UPDATE:
937                         synchronized (mLock) {
938                             updateOpportunisticSubscriptions();
939                             enableModemStackForNonOpportunisticSlots();
940                         }
941                         break;
942                     case MSG_START_PROFILE_SELECTION:
943                         logDebug("Msg received for profile update");
944                         synchronized (mLock) {
945                             checkProfileUpdate((Object[]) msg.obj);
946                         }
947                         break;
948                     case MSG_STOP_PROFILE_SELECTION:
949                         logDebug("Msg received to stop profile selection");
950                         synchronized (mLock) {
951                             stopProfileSelectionProcess((IUpdateAvailableNetworksCallback) msg.obj);
952                         }
953                         break;
954                     case MSG_SUB_SWITCH_COMPLETE:
955                         logDebug("Msg received for sub switch");
956                         synchronized (mLock) {
957                             onSubSwitchComplete((int) msg.obj);
958                         }
959                         break;
960                     default:
961                         log("invalid message");
962                         break;
963                 }
964             }
965         };
966         /* register for profile update events */
967         mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener(
968                 AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener);
969     }
970 
log(String msg)971     private void log(String msg) {
972         Rlog.d(LOG_TAG, msg);
973     }
974 
logDebug(String msg)975     private void logDebug(String msg) {
976         if (DBG) {
977             Rlog.d(LOG_TAG, msg);
978         }
979     }
980 }
981