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