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

Commit ca8a69bd authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Create EuiccRepository" into main

parents 5247f493 f74e9078
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