• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.annotation.NonNull;
20 import android.annotation.TestApi;
21 import android.content.Context;
22 import android.net.ConnectivityManager;
23 import android.net.Network;
24 import android.net.NetworkCapabilities;
25 import android.net.NetworkRequest;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.os.ParcelUuid;
30 import android.os.PersistableBundle;
31 import android.telephony.CarrierConfigManager;
32 import android.telephony.SubscriptionInfo;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.telephony.UiccCardInfo;
36 import android.telephony.euicc.EuiccManager;
37 import android.util.Log;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.telephony.flags.Flags;
41 import com.android.ons.ONSProfileDownloader.DownloadRetryResultCode;
42 
43 import java.io.FileDescriptor;
44 import java.io.PrintWriter;
45 import java.util.List;
46 import java.util.Random;
47 
48 /**
49  * ONSProfileActivator makes sure that the CBRS profile is downloaded, activated and grouped
50  * when an opportunistic data enabled pSIM is inserted.
51  */
52 public class ONSProfileActivator implements ONSProfileConfigurator.ONSProfConfigListener,
53         ONSProfileDownloader.IONSProfileDownloaderListener {
54 
55     private static final String TAG = ONSProfileActivator.class.getName();
56     private final Context mContext;
57     private final SubscriptionManager mSubManager;
58     private final TelephonyManager mTelephonyManager;
59     private final CarrierConfigManager mCarrierConfigMgr;
60     private final EuiccManager mEuiccManager;
61     private final ONSProfileConfigurator mONSProfileConfig;
62     private final ONSProfileDownloader mONSProfileDownloader;
63     private final ConnectivityManager mConnectivityManager;
64     private final ONSStats mONSStats;
65     @VisibleForTesting protected boolean mIsInternetConnAvailable = false;
66     @VisibleForTesting protected boolean mRetryDownloadWhenNWConnected = false;
67     @VisibleForTesting protected int mDownloadRetryCount = 0;
68 
69     @VisibleForTesting protected static final int REQUEST_CODE_DOWNLOAD_RETRY = 2;
70 
ONSProfileActivator(Context context, ONSStats onsStats)71     public ONSProfileActivator(Context context, ONSStats onsStats) {
72         mContext = context;
73         SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
74         if (Flags.workProfileApiSplit()) {
75             sm = sm.createForAllUserProfiles();
76         }
77         mSubManager = sm;
78         mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
79         mCarrierConfigMgr = mContext.getSystemService(CarrierConfigManager.class);
80         mEuiccManager = mContext.getSystemService(EuiccManager.class);
81         mONSProfileConfig = new ONSProfileConfigurator(mContext, mSubManager,
82                 mCarrierConfigMgr, mEuiccManager, this);
83         mONSProfileDownloader =
84                 new ONSProfileDownloader(
85                         mContext, mCarrierConfigMgr, mEuiccManager, mSubManager, this);
86 
87         //Monitor internet connection.
88         mConnectivityManager = context.getSystemService(ConnectivityManager.class);
89         mONSStats = onsStats;
90         NetworkRequest request = new NetworkRequest.Builder().addCapability(
91                 NetworkCapabilities.NET_CAPABILITY_VALIDATED).build();
92         mConnectivityManager.registerNetworkCallback(request, new NetworkCallback());
93     }
94 
95     /**
96      * This constructor is only for JUnit testing
97      */
98     @TestApi
ONSProfileActivator(Context mockContext, SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, CarrierConfigManager carrierConfigMgr, EuiccManager euiccManager, ConnectivityManager connManager, ONSProfileConfigurator onsProfileConfigurator, ONSProfileDownloader onsProfileDownloader, ONSStats onsStats)99     ONSProfileActivator(Context mockContext, SubscriptionManager subscriptionManager,
100                         TelephonyManager telephonyManager, CarrierConfigManager carrierConfigMgr,
101                         EuiccManager euiccManager, ConnectivityManager connManager,
102                         ONSProfileConfigurator onsProfileConfigurator,
103                         ONSProfileDownloader onsProfileDownloader, ONSStats onsStats) {
104         mContext = mockContext;
105         mSubManager = subscriptionManager;
106         mTelephonyManager = telephonyManager;
107         mCarrierConfigMgr = carrierConfigMgr;
108         mEuiccManager = euiccManager;
109         mConnectivityManager = connManager;
110         mONSProfileConfig = onsProfileConfigurator;
111         mONSProfileDownloader = onsProfileDownloader;
112         mONSStats = onsStats;
113     }
114 
getONSProfileConfigurator()115     ONSProfileConfigurator getONSProfileConfigurator() {
116         return mONSProfileConfig;
117     }
118 
getONSProfileDownloader()119     ONSProfileDownloader getONSProfileDownloader() {
120         return mONSProfileDownloader;
121     }
122 
123     private final Handler mHandler = new Handler(Looper.myLooper()) {
124         @Override
125         public void handleMessage(Message msg) {
126             switch (msg.what) {
127                 case REQUEST_CODE_DOWNLOAD_RETRY: {
128                     Result res = provisionCBRS();
129                     Log.d(TAG, res.toString());
130                     mONSStats.logEvent(
131                             new ONSStatsInfo.Builder()
132                                     .setProvisioningResult(mContext, res)
133                                     .build());
134                 }
135                 break;
136             }
137         }
138     };
139 
140     /**
141      * Called when SIM state changes. Triggers CBRS Auto provisioning.
142      */
handleCarrierConfigChange()143     public Result handleCarrierConfigChange() {
144         Result res = provisionCBRS();
145         Log.d(TAG, res.toString());
146         mONSStats.logEvent(new ONSStatsInfo.Builder().setProvisioningResult(mContext, res).build());
147 
148         // Reset mDownloadRetryCount as carrier config change event is received. Either new SIM card
149         // is inserted or carrier config values are updated.
150         if (res == Result.DOWNLOAD_REQUESTED || res == Result.SUCCESS) {
151             mDownloadRetryCount = 0;
152         }
153 
154         return res;
155     }
156 
157     @Override
onOppSubscriptionDeleted(int pSIMId)158     public void onOppSubscriptionDeleted(int pSIMId) {
159         Result res = provisionCBRS();
160         Log.d(TAG, res.toString());
161         mONSStats.logEvent(new ONSStatsInfo.Builder().setProvisioningResult(mContext, res).build());
162     }
163 
164     /**
165      * Checks if AutoProvisioning is enabled, MultiSIM and eSIM support, cbrs pSIM is inserted and
166      * makes sure device is in muti-SIM mode before triggering download of opportunistic eSIM.
167      * Once downloaded, groups with pSIM, sets opportunistic and activates.
168      */
provisionCBRS()169     private Result provisionCBRS() {
170 
171         if (!isONSAutoProvisioningEnabled()) {
172             return Result.ERR_AUTO_PROVISIONING_DISABLED;
173         }
174 
175         //Check if device supports eSIM
176         if (!isESIMSupported()) {
177             return Result.ERR_ESIM_NOT_SUPPORTED;
178         }
179 
180         //Check if it's a multi SIM Phone. CBRS is not supported on Single SIM phone.
181         if (!isMultiSIMPhone()) {
182             return Result.ERR_MULTISIM_NOT_SUPPORTED;
183         }
184 
185         //Check the number of active subscriptions.
186         List<SubscriptionInfo> activeSubInfos = mSubManager.getActiveSubscriptionInfoList();
187         if (activeSubInfos == null || activeSubInfos.isEmpty()) {
188             return Result.ERR_NO_SIM_INSERTED;
189         }
190         int activeSubCount = activeSubInfos.size();
191         Log.d(TAG, "Active subscription count:" + activeSubCount);
192 
193         if (activeSubCount == 1) {
194             SubscriptionInfo pSubInfo = activeSubInfos.getFirst();
195             if (pSubInfo.isOpportunistic()) {
196                 //Only one SIM is active and its opportunistic SIM.
197                 //Opportunistic eSIM shouldn't be used without pSIM.
198                 return Result.ERR_SINGLE_ACTIVE_OPPORTUNISTIC_SIM;
199             }
200 
201             //if pSIM is not a CBRS carrier
202             if (!isOppDataAutoProvisioningSupported(
203                     pSubInfo.getSubscriptionId())) {
204                 return Result.ERR_CARRIER_DOESNT_SUPPORT_CBRS;
205             }
206 
207             if (isDeviceInSingleSIMMode()) {
208                 if (!switchToMultiSIMMode()) {
209                     return Result.ERR_CANNOT_SWITCH_TO_DUAL_SIM_MODE;
210                 }
211 
212                 //Once device is Switched to Dual-SIM Mode, handleSimStateChange is triggered.
213                 return Result.ERR_SWITCHING_TO_DUAL_SIM_MODE;
214             }
215 
216             return downloadAndActivateOpportunisticSubscription(pSubInfo);
217         } else if (activeSubCount >= 2) {
218             //If all the SIMs are physical SIM then it's a sure case of DUAL Active Subscription.
219             boolean allPhysicalSIMs = true;
220             for (SubscriptionInfo subInfo : activeSubInfos) {
221                 if (subInfo.isEmbedded()) {
222                     allPhysicalSIMs = false;
223                     break;
224                 }
225             }
226 
227             if (allPhysicalSIMs) {
228                 return Result.ERR_DUAL_ACTIVE_SUBSCRIPTIONS;
229             }
230 
231             //Check if one of the subscription is opportunistic but not marked.
232             //if one of the SIM is opportunistic and not grouped then group the subscription.
233             for (SubscriptionInfo subInfo : activeSubInfos) {
234                 int pSubId = subInfo.getSubscriptionId();
235                 if (!subInfo.isEmbedded() && isOppDataAutoProvisioningSupported(pSubId)) {
236 
237                     Log.d(TAG, "CBRS pSIM found. SubId:" + pSubId);
238 
239                     //Check if other SIM is opportunistic based on carrier-id.
240                     SubscriptionInfo oppSubInfo = mONSProfileConfig
241                             .findOpportunisticSubscription(pSubId);
242 
243                     //If opportunistic eSIM is found and activated.
244                     if (oppSubInfo != null) {
245                         if (mSubManager.isActiveSubscriptionId(oppSubInfo.getSubscriptionId())
246                                 && oppSubInfo.isOpportunistic()) {
247                             //Already configured. No action required.
248                             return Result.SUCCESS;
249                         }
250 
251                         ParcelUuid pSIMGroupId = mONSProfileConfig.getPSIMGroupId(subInfo);
252                         mONSProfileConfig.groupWithPSIMAndSetOpportunistic(oppSubInfo, pSIMGroupId);
253                         return Result.SUCCESS;
254                     }
255                 }
256             }
257 
258             return Result.ERR_DUAL_ACTIVE_SUBSCRIPTIONS;
259         }
260 
261         return Result.ERR_UNKNOWN;
262     }
263 
downloadAndActivateOpportunisticSubscription( SubscriptionInfo primaryCBRSSubInfo)264     private Result downloadAndActivateOpportunisticSubscription(
265             SubscriptionInfo primaryCBRSSubInfo) {
266         Log.d(TAG, "downloadAndActivateOpportunisticSubscription");
267 
268         //Check if pSIM is part of a group. If not then create a group.
269         ParcelUuid pSIMgroupId = mONSProfileConfig.getPSIMGroupId(primaryCBRSSubInfo);
270 
271         //Check if opp eSIM is already downloaded but not grouped.
272         SubscriptionInfo oppSubInfo = mONSProfileConfig.findOpportunisticSubscription(
273                 primaryCBRSSubInfo.getSubscriptionId());
274         if (oppSubInfo != null) {
275             mONSProfileConfig.groupWithPSIMAndSetOpportunistic(oppSubInfo, pSIMgroupId);
276             return Result.SUCCESS;
277         }
278 
279         if (!mIsInternetConnAvailable) {
280             Log.d(TAG, "No internet connection. Download will be attempted when "
281                     + "connection is restored");
282             mRetryDownloadWhenNWConnected = true;
283             return Result.ERR_WAITING_FOR_INTERNET_CONNECTION;
284         }
285 
286         /* If download WiFi only flag is set and WiFi is not connected */
287         if (getESIMDownloadViaWiFiOnlyFlag(primaryCBRSSubInfo.getSubscriptionId())
288                 && !isWiFiConnected()) {
289             Log.d(TAG, "Download via WiFi only flag is set but WiFi is not connected."
290                     + "Download will be attempted when WiFi connection is restored");
291             mRetryDownloadWhenNWConnected = true;
292             return Result.ERR_WAITING_FOR_WIFI_CONNECTION;
293         }
294 
295         //Opportunistic subscription not found. Trigger Download.
296         ONSProfileDownloader.DownloadProfileResult res = mONSProfileDownloader.downloadProfile(
297                 primaryCBRSSubInfo.getSubscriptionId());
298 
299         return switch (res) {
300             case DUPLICATE_REQUEST -> Result.ERR_DUPLICATE_DOWNLOAD_REQUEST;
301             case INVALID_SMDP_ADDRESS -> Result.ERR_INVALID_CARRIER_CONFIG;
302             case SUCCESS -> Result.DOWNLOAD_REQUESTED;
303         };
304 
305     }
306 
307     @Override
onDownloadComplete(int primarySubId)308     public void onDownloadComplete(int primarySubId) {
309         mRetryDownloadWhenNWConnected = false;
310         SubscriptionInfo opportunisticESIM = mONSProfileConfig.findOpportunisticSubscription(
311                 primarySubId);
312         if (opportunisticESIM == null) {
313             Log.e(TAG, "Downloaded Opportunistic eSIM not found. Unable to group with pSIM");
314             mONSStats.logEvent(new ONSStatsInfo.Builder()
315                     .setProvisioningResult(mContext, Result.ERR_DOWNLOADED_ESIM_NOT_FOUND)
316                     .setPrimarySimSubId(primarySubId)
317                     .setWifiConnected(isWiFiConnected())
318                     .build());
319             return;
320         }
321 
322         SubscriptionInfo pSIMSubInfo = mSubManager.getActiveSubscriptionInfo(primarySubId);
323         if (pSIMSubInfo != null) {
324             // Group with same Primary SIM for which eSIM is downloaded.
325             mONSProfileConfig.groupWithPSIMAndSetOpportunistic(
326                     opportunisticESIM, pSIMSubInfo.getGroupUuid());
327             Log.d(TAG, "eSIM downloaded and configured successfully");
328             mONSStats.logEvent(new ONSStatsInfo.Builder()
329                     .setProvisioningResult(mContext, Result.SUCCESS)
330                     .setRetryCount(mDownloadRetryCount)
331                     .setWifiConnected(isWiFiConnected())
332                     .build());
333         } else {
334             Log.d(TAG, "ESIM downloaded but pSIM is not active or removed");
335             mONSStats.logEvent(new ONSStatsInfo.Builder()
336                     .setProvisioningResult(mContext, Result.ERR_PSIM_NOT_FOUND)
337                     .setOppSimCarrierId(opportunisticESIM.getCarrierId())
338                     .setWifiConnected(isWiFiConnected())
339                     .build());
340         }
341     }
342 
343     @Override
onDownloadError(int pSIMSubId, DownloadRetryResultCode resultCode, int detailedErrorCode)344     public void onDownloadError(int pSIMSubId, DownloadRetryResultCode resultCode,
345             int detailedErrorCode) {
346         boolean logStats = true;
347         switch (resultCode) {
348             case ERR_MEMORY_FULL: {
349                 //eUICC Memory full occurred while downloading opportunistic eSIM.
350 
351                 //First find and delete any opportunistic eSIMs from the operator same as the
352                 // current primary SIM.
353                 List<Integer> oppSubIds = mONSProfileConfig
354                         .getOpportunisticSubIdsofPSIMOperator(pSIMSubId);
355                 if (oppSubIds != null && !oppSubIds.isEmpty()) {
356                     mONSProfileConfig.deleteSubscription(oppSubIds.getFirst());
357                 } else {
358                     //else, find the inactive opportunistic eSIMs (any operator) and delete one of
359                     // them and retry download again.
360                     mONSProfileConfig.deleteInactiveOpportunisticSubscriptions(pSIMSubId);
361                 }
362 
363                 //Delete subscription -> onOppSubscriptionDeleted callback ->  provisionCBRS ->
364                 // triggers eSIM download again.
365 
366                 //Download retry will stop if there are no opportunistic eSIM profiles to delete.
367             }
368             break;
369 
370             case ERR_INSTALL_ESIM_PROFILE_FAILED: {
371                 //Since the installation of eSIM profile has failed there may be an issue with the
372                 //format or profile data. We retry by first deleting existing eSIM profile from the
373                 //operator same as the primary SIM and retry download opportunistic eSIM.
374                 List<Integer> oppSubIds = mONSProfileConfig
375                         .getOpportunisticSubIdsofPSIMOperator(pSIMSubId);
376 
377                 if (oppSubIds != null && !oppSubIds.isEmpty()) {
378                     mONSProfileConfig.deleteSubscription(oppSubIds.getFirst());
379                 }
380 
381                 //Download retry will stop if there are no opportunistic eSIM profiles to delete
382                 // from the same operator.
383             }
384             break;
385 
386             case ERR_RETRY_DOWNLOAD: {
387                 if (startBackoffTimer(pSIMSubId)) {
388                     // do not log the atom if download retry has not reached max limit.
389                     logStats = false;
390                 }
391             }
392             break;
393             default: {
394                 // Stop download until SIM change or device reboot.
395                 Log.e(TAG, "Download failed with cause=" + resultCode);
396             }
397         }
398         if (logStats) {
399             mONSStats.logEvent(new ONSStatsInfo.Builder()
400                     .setDownloadResult(resultCode)
401                     .setPrimarySimSubId(pSIMSubId)
402                     .setRetryCount(mDownloadRetryCount)
403                     .setDetailedErrCode(detailedErrorCode)
404                     .setWifiConnected(isWiFiConnected())
405                     .build());
406         }
407     }
408 
409     /**
410      * Called when eSIM download fails. Listener is called after a delay based on retry count with
411      * the error code: BACKOFF_TIMER_EXPIRED
412      *
413      * @param pSIMSubId Primary Subscription ID
414      * @return true if backoff timer starts; otherwise false.
415      */
416     @VisibleForTesting
startBackoffTimer(int pSIMSubId)417     protected boolean startBackoffTimer(int pSIMSubId) {
418         //retry logic
419         mDownloadRetryCount++;
420         Log.e(TAG, "Download retry count :" + mDownloadRetryCount);
421 
422         //Stop download retry if number of retries exceeded max configured value.
423         if (mDownloadRetryCount > getDownloadRetryMaxAttemptsVal(pSIMSubId)) {
424             Log.e(TAG, "Max download retry attempted. Stopping retry");
425             return false;
426         }
427 
428         int backoffTimerVal = getDownloadRetryBackOffTimerVal(pSIMSubId);
429         int delay = calculateBackoffDelay(mDownloadRetryCount, backoffTimerVal);
430 
431         Message retryMsg = new Message();
432         retryMsg.what = REQUEST_CODE_DOWNLOAD_RETRY;
433         retryMsg.arg2 = pSIMSubId;
434         mHandler.sendMessageDelayed(retryMsg, delay);
435 
436         Log.d(TAG, "Download failed. Retry after :" + delay + "MilliSecs");
437         return true;
438     }
439 
440     @VisibleForTesting
calculateBackoffDelay(int retryCount, int backoffTimerVal)441     protected static int calculateBackoffDelay(int retryCount, int backoffTimerVal) {
442         /**
443          * Timer value is calculated using "Exponential Backoff retry" algorithm.
444          * When the first download failure occurs, retry download after
445          * BACKOFF_TIMER_VALUE [Carrier Configurable] seconds.
446          *
447          * If download fails again then, retry after either BACKOFF_TIMER_VALUE,
448          * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.
449          *
450          * In general after the cth failed attempt, retry after k *
451          * BACKOFF_TIMER_VALUE seconds, where k is a random integer between 1 and
452          * 2^c − 1. Max c value is KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT
453          * [Carrier configurable]
454          */
455         Random random = new Random();
456         //Calculate 2^c − 1
457         int maxTime = (int) Math.pow(2, retryCount) - 1;
458 
459         //Random value between (1 & 2^c − 1) and convert to millisecond
460         return ((random.nextInt(maxTime) + 1)) * backoffTimerVal * 1000;
461     }
462 
463     /**
464      * Retrieves maximum retry attempts from carrier configuration. After maximum attempts, further
465      * attempts will not be made until next device reboot.
466      *
467      * @param subscriptionId subscription Id of the primary SIM.
468      * @return integer value for maximum allowed retry attempts.
469      */
getDownloadRetryMaxAttemptsVal(int subscriptionId)470     private int getDownloadRetryMaxAttemptsVal(int subscriptionId) {
471         PersistableBundle config = mCarrierConfigMgr.getConfigForSubId(subscriptionId);
472         return config.getInt(CarrierConfigManager.KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT);
473     }
474 
475     /**
476      * Retrieves backoff timer value (in seconds) from carrier configuration. Value is used to
477      * calculate delay before retrying profile download.
478      *
479      * @param subscriptionId subscription Id of the primary SIM.
480      * @return Backoff timer value in seconds.
481      */
getDownloadRetryBackOffTimerVal(int subscriptionId)482     private int getDownloadRetryBackOffTimerVal(int subscriptionId) {
483         PersistableBundle config = mCarrierConfigMgr.getConfigForSubId(subscriptionId);
484         return config.getInt(CarrierConfigManager.KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT);
485     }
486 
487 
488     /**
489      * Checks if device supports eSIM.
490      */
isESIMSupported()491     private boolean isESIMSupported() {
492         for (UiccCardInfo uiccCardInfo : mTelephonyManager.getUiccCardsInfo()) {
493             if (uiccCardInfo != null && !uiccCardInfo.isEuicc()) {
494                 // Skip this card.
495                 continue;
496             }
497 
498             EuiccManager euiccManager = mEuiccManager.createForCardId(uiccCardInfo.getCardId());
499             if (euiccManager.isEnabled()) {
500                 return true;
501             }
502         }
503 
504         return false;
505     }
506 
507     /**
508      * Fetches ONS auto provisioning enable flag from device configuration.
509      * ONS auto provisioning feature executes only when the flag is set to true in device
510      * configuration.
511      */
isONSAutoProvisioningEnabled()512     private boolean isONSAutoProvisioningEnabled() {
513         return mContext.getResources().getBoolean(R.bool.enable_ons_auto_provisioning);
514     }
515 
516     /**
517      * Check if device support multiple active SIMs
518      */
isMultiSIMPhone()519     private boolean isMultiSIMPhone() {
520         return (mTelephonyManager.getSupportedModemCount() >= 2);
521     }
522 
523     /**
524      * Check if the given subscription is a CBRS supported carrier.
525      */
isOppDataAutoProvisioningSupported(int pSIMSubId)526     private boolean isOppDataAutoProvisioningSupported(int pSIMSubId) {
527         PersistableBundle config = mCarrierConfigMgr.getConfigForSubId(pSIMSubId);
528         return config.getBoolean(CarrierConfigManager
529                 .KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL);
530     }
531 
532     /**
533      * Checks if device is in single SIM mode.
534      */
isDeviceInSingleSIMMode()535     private boolean isDeviceInSingleSIMMode() {
536         return (mTelephonyManager.getActiveModemCount() <= 1);
537     }
538 
539     /**
540      * Switches device to multi SIM mode. Checks if reboot is required before switching and
541      * configuration is triggered only if reboot not required.
542      */
switchToMultiSIMMode()543     private boolean switchToMultiSIMMode() {
544         if (!mTelephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
545             mTelephonyManager.switchMultiSimConfig(2);
546             return true;
547         }
548 
549         return false;
550     }
551 
isWiFiConnected()552     private boolean isWiFiConnected() {
553         Network activeNetwork = mConnectivityManager.getActiveNetwork();
554         return (activeNetwork != null) && mConnectivityManager.getNetworkCapabilities(activeNetwork)
555                 .hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
556     }
557 
558     /**
559      * Retrieves WiFi only eSIM Download flag the given subscription from carrier configuration.
560      *
561      * @param subscriptionId subscription Id of the primary SIM.
562      * @return download flag.
563      */
getESIMDownloadViaWiFiOnlyFlag(int subscriptionId)564     private boolean getESIMDownloadViaWiFiOnlyFlag(int subscriptionId) {
565         PersistableBundle config = mCarrierConfigMgr.getConfigForSubId(subscriptionId);
566         return config.getBoolean(
567                 CarrierConfigManager.KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL);
568     }
569 
570     private class NetworkCallback extends ConnectivityManager.NetworkCallback {
571         @Override
onAvailable(Network network)572         public void onAvailable(Network network) {
573             super.onAvailable(network);
574             Log.d(TAG, "Internet connection available");
575             mIsInternetConnAvailable = true;
576             if (mRetryDownloadWhenNWConnected) {
577                 Result res = provisionCBRS();
578                 Log.d(TAG, res.toString());
579                 mONSStats.logEvent(new ONSStatsInfo.Builder()
580                         .setProvisioningResult(mContext, res)
581                         .build());
582             }
583         }
584 
585         @Override
onLost(Network network)586         public void onLost(Network network) {
587             super.onLost(network);
588             Log.d(TAG, "Internet connection lost");
589             mIsInternetConnAvailable = false;
590         }
591     }
592 
593     /**
594      * Enum to map the results of the CBRS provisioning. The order of the defined enums must be kept
595      * intact and new entries should be appended at the end of the list.
596      */
597     public enum Result {
598         SUCCESS,
599         DOWNLOAD_REQUESTED,
600         ERR_SWITCHING_TO_DUAL_SIM_MODE,
601         ERR_AUTO_PROVISIONING_DISABLED,
602         ERR_ESIM_NOT_SUPPORTED,
603         ERR_MULTISIM_NOT_SUPPORTED,
604         ERR_CARRIER_DOESNT_SUPPORT_CBRS,
605         ERR_DUAL_ACTIVE_SUBSCRIPTIONS,
606         ERR_NO_SIM_INSERTED,
607         ERR_SINGLE_ACTIVE_OPPORTUNISTIC_SIM,
608         ERR_CANNOT_SWITCH_TO_DUAL_SIM_MODE,
609         ERR_WAITING_FOR_INTERNET_CONNECTION,
610         ERR_WAITING_FOR_WIFI_CONNECTION,
611         ERR_DUPLICATE_DOWNLOAD_REQUEST,
612         ERR_INVALID_CARRIER_CONFIG,
613         ERR_DOWNLOADED_ESIM_NOT_FOUND,
614         ERR_PSIM_NOT_FOUND,
615         ERR_UNKNOWN
616     }
617 
618     /**
619      * Dump the state of {@link ONSProfileActivator}.
620      */
dump(@onNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args)621     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
622             @NonNull String[] args) {
623         pw.println(TAG + ":");
624         pw.println("  mIsInternetConnAvailable: " + mIsInternetConnAvailable);
625         pw.println("  mRetryDownloadWhenNWConnected: " + mRetryDownloadWhenNWConnected);
626         pw.println("  mDownloadRetryCount: " + mDownloadRetryCount);
627         mONSProfileDownloader.dump(fd, pw, args);
628     }
629 }
630