Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f74e9078 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Create EuiccRepository

Which also avoid calling from main thread.

Bug: 356684993
Flag: EXEMPT bug fix
Test: manual - on SIMs
Test: atest EuiccRepositoryTest
Change-Id: I0b11b0bd1e8a4b5754781e888fd220fa3080a212
parent 77292869
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ import com.android.settings.R
import com.android.settings.SettingsPreferenceFragment
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.flags.Flags
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.euicc.EuiccRepository
import com.android.settings.search.BaseSearchIndexProvider
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.network.NetworkCellularGroupProvider
@@ -58,7 +58,7 @@ class MobileNetworkListFragment : DashboardFragment() {
        listView.itemAnimator = null

        findPreference<Preference>(KEY_ADD_SIM)!!.isVisible =
            MobileNetworkUtils.showEuiccSettings(context)
            EuiccRepository(requireContext()).showEuiccSettings()
    }

    override fun getPreferenceScreenResId() = R.xml.network_provider_sims_list
+3 −3
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.euicc.EuiccRepository;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.Utils;
@@ -118,7 +118,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
        if ((mSubInfoEntityList == null || mSubInfoEntityList.isEmpty()) || (
                mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty()) || (
                mMobileNetworkInfoEntityList == null || mMobileNetworkInfoEntityList.isEmpty())) {
            if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) {
            if (new EuiccRepository(mContext).showEuiccSettings()) {
                return mContext.getResources().getString(
                        R.string.mobile_network_summary_add_a_network);
            }
@@ -168,7 +168,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
                || (mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty())
                || (mMobileNetworkInfoEntityList == null
                || mMobileNetworkInfoEntityList.isEmpty()))) {
            if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) {
            if (new EuiccRepository(mContext).showEuiccSettings()) {
                mPreference.setOnPreferenceClickListener((Preference pref) -> {
                    logPreferenceClick(pref);
                    startAddSimFlow();
+0 −76
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import static com.android.settings.network.telephony.TelephonyConstants.Telephon
import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;

import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -51,8 +50,6 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -89,32 +86,17 @@ import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import com.android.settingslib.utils.ThreadUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class MobileNetworkUtils {

    private static final String TAG = "MobileNetworkUtils";

    // CID of the device.
    private static final String KEY_CID = "ro.boot.cid";
    // CIDs of devices which should not show anything related to eSIM.
    private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
    // System Property which is used to decide whether the default eSIM UI will be shown,
    // the default value is false.
    private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
            "esim.enable_esim_system_ui_by_default";
    private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
            "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
    private static final String RTL_MARK = "\u200F";
@@ -282,64 +264,6 @@ public class MobileNetworkUtils {
        return intent;
    }

    /**
     * Whether to show the entry point to eUICC settings.
     *
     * <p>We show the entry point on any device which supports eUICC as long as either the eUICC
     * was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
     * the user has enabled development mode.
     */
    public static boolean showEuiccSettings(Context context) {
        if (!SubscriptionUtil.isSimHardwareVisible(context)) {
            return false;
        }
        long timeForAccess = SystemClock.elapsedRealtime();
        try {
            Boolean isShow = ((Future<Boolean>) ThreadUtils.postOnBackgroundThread(() -> {
                        try {
                            return showEuiccSettingsDetecting(context);
                        } catch (Exception threadException) {
                            Log.w(TAG, "Accessing Euicc failure", threadException);
                        }
                        return Boolean.FALSE;
                    })).get(3, TimeUnit.SECONDS);
            return ((isShow != null) && isShow.booleanValue());
        } catch (ExecutionException | InterruptedException | TimeoutException exception) {
            timeForAccess = SystemClock.elapsedRealtime() - timeForAccess;
            Log.w(TAG, "Accessing Euicc takes too long: +" + timeForAccess + "ms");
        }
        return false;
    }

    // The same as #showEuiccSettings(Context context)
    public static Boolean showEuiccSettingsDetecting(Context context) {
        final EuiccManager euiccManager =
                (EuiccManager) context.getSystemService(EuiccManager.class);
        if (euiccManager == null || !euiccManager.isEnabled()) {
            Log.w(TAG, "EuiccManager is not enabled.");
            return false;
        }

        final ContentResolver cr = context.getContentResolver();
        final boolean esimIgnoredDevice =
                Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
                        .contains(SystemProperties.get(KEY_CID));
        final boolean enabledEsimUiByDefault =
                SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
        final boolean euiccProvisioned =
                Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
        final boolean inDeveloperMode =
                DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
        Log.i(TAG,
                String.format("showEuiccSettings: esimIgnoredDevice: %b, enabledEsimUiByDefault: "
                        + "%b, euiccProvisioned: %b, inDeveloperMode: %b.",
                esimIgnoredDevice, enabledEsimUiByDefault, euiccProvisioned, inDeveloperMode));
        return (euiccProvisioned
                || (!esimIgnoredDevice && inDeveloperMode)
                || (!esimIgnoredDevice && enabledEsimUiByDefault
                        && isCurrentCountrySupported(context)));
    }

    /**
     * Return {@code true} if mobile data is enabled
     */
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.network.telephony.euicc

import android.content.Context
import android.os.SystemProperties
import android.provider.Settings
import android.telephony.TelephonyManager
import android.telephony.euicc.EuiccManager
import android.util.Log
import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.development.DevelopmentSettingsEnabler
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn

class EuiccRepository
@JvmOverloads
constructor(
    private val context: Context,
    private val isEuiccProvisioned: () -> Boolean = {
        val euiccProvisioned by context.settingsGlobalBoolean(Settings.Global.EUICC_PROVISIONED)
        euiccProvisioned
    },
    private val isDevelopmentSettingsEnabled: () -> Boolean = {
        DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
    },
) {

    private val euiccManager = context.getSystemService(EuiccManager::class.java)
    private val telephonyManager = context.getSystemService(TelephonyManager::class.java)

    fun showEuiccSettingsFlow() =
        flow { emit(showEuiccSettings()) }
            .distinctUntilChanged()
            .conflate()
            .flowOn(Dispatchers.Default)

    /**
     * Whether to show the entry point to eUICC settings.
     *
     * We show the entry point on any device which supports eUICC as long as either the eUICC was
     * ever provisioned (that is, at least one profile was ever downloaded onto it), or if the user
     * has enabled development mode.
     */
    fun showEuiccSettings(): Boolean {
        if (!SubscriptionUtil.isSimHardwareVisible(context)) return false
        if (euiccManager == null || !euiccManager.isEnabled) {
            Log.w(TAG, "EuiccManager is not enabled.")
            return false
        }
        if (isEuiccProvisioned()) {
            Log.i(TAG, "showEuiccSettings: euicc provisioned")
            return true
        }
        val ignoredCids =
            SystemProperties.get(KEY_ESIM_CID_IGNORE).split(',').filter { it.isNotEmpty() }
        val cid = SystemProperties.get(KEY_CID)
        if (cid in ignoredCids) {
            Log.i(TAG, "showEuiccSettings: cid ignored")
            return false
        }
        if (isDevelopmentSettingsEnabled()) {
            Log.i(TAG, "showEuiccSettings: development settings enabled")
            return true
        }
        val enabledEsimUiByDefault =
            SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true)
        Log.i(TAG, "showEuiccSettings: enabledEsimUiByDefault=$enabledEsimUiByDefault")
        return enabledEsimUiByDefault && isCurrentCountrySupported()
    }

    /**
     * Loop through all the device logical slots to check whether the user's current country
     * supports eSIM.
     */
    private fun isCurrentCountrySupported(): Boolean {
        val euiccManager = euiccManager ?: return false
        val telephonyManager = telephonyManager ?: return false
        val visitedCountrySet = mutableSetOf<String>()
        for (slotIndex in 0 until telephonyManager.getActiveModemCount()) {
            val countryCode = telephonyManager.getNetworkCountryIso(slotIndex)
            if (
                countryCode.isNotEmpty() &&
                    visitedCountrySet.add(countryCode) &&
                    euiccManager.isSupportedCountry(countryCode)
            ) {
                Log.i(TAG, "isCurrentCountrySupported: $countryCode is supported")
                return true
            }
        }
        Log.i(TAG, "isCurrentCountrySupported: no country is supported")
        return false
    }

    companion object {
        private const val TAG = "EuiccRepository"

        /** CID of the device. */
        private const val KEY_CID: String = "ro.boot.cid"

        /** CIDs of devices which should not show anything related to eSIM. */
        private const val KEY_ESIM_CID_IGNORE: String = "ro.setupwizard.esim_cid_ignore"

        /**
         * System Property which is used to decide whether the default eSIM UI will be shown, the
         * default value is false.
         */
        private const val KEY_ENABLE_ESIM_UI_BY_DEFAULT: String =
            "esim.enable_esim_system_ui_by_default"
    }
}
+11 −6
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.SubscriptionActivationRepository
import com.android.settings.network.telephony.SubscriptionRepository
import com.android.settings.network.telephony.euicc.EuiccRepository
import com.android.settings.network.telephony.phoneNumberFlow
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -120,9 +121,13 @@ fun phoneNumber(subInfo: SubscriptionInfo): State<String?> {
@Composable
private fun AddSim() {
    val context = LocalContext.current
    if (remember { MobileNetworkUtils.showEuiccSettings(context) }) {
    val isShow by
        remember { EuiccRepository(context).showEuiccSettingsFlow() }
            .collectAsStateWithLifecycle(initialValue = false)
    if (isShow) {
        RestrictedPreference(
            model = object : PreferenceModel {
            model =
                object : PreferenceModel {
                    override val title = stringResource(id = R.string.mobile_network_list_add_more)
                    override val icon = @Composable { SettingsIcon(Icons.Outlined.Add) }
                    override val onClick = { startAddSimFlow(context) }
Loading