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
This commit is contained in:
Chaohui Wang
2024-08-22 16:49:37 +08:00
parent 772928694f
commit f74e90787e
6 changed files with 262 additions and 87 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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
*/

View File

@@ -0,0 +1,129 @@
/*
* 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"
}
}