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