Merge "Remove all eSIM Invisible Profiles during FDR" into qt-r1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
19a91def43
@@ -32,6 +32,8 @@ import android.content.IntentFilter;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.storage.IStorageManager;
|
import android.os.storage.IStorageManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.telephony.SubscriptionInfo;
|
||||||
|
import android.telephony.SubscriptionManager;
|
||||||
import android.telephony.euicc.EuiccManager;
|
import android.telephony.euicc.EuiccManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
@@ -59,10 +61,12 @@ import java.security.cert.X509Certificate;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@@ -90,11 +94,14 @@ public class RecoverySystem {
|
|||||||
private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
|
private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
|
||||||
|
|
||||||
private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s
|
private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s
|
||||||
|
|
||||||
private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s
|
private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s
|
||||||
|
|
||||||
private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s
|
private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s
|
||||||
|
|
||||||
|
private static final long DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
|
||||||
|
45000L; // 45 s
|
||||||
|
private static final long MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 15000L; // 15 s
|
||||||
|
private static final long MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 90000L; // 90 s
|
||||||
|
|
||||||
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
|
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
|
||||||
private static final File RECOVERY_DIR = new File("/cache/recovery");
|
private static final File RECOVERY_DIR = new File("/cache/recovery");
|
||||||
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
|
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
|
||||||
@@ -102,9 +109,14 @@ public class RecoverySystem {
|
|||||||
private static final String LAST_PREFIX = "last_";
|
private static final String LAST_PREFIX = "last_";
|
||||||
private static final String ACTION_EUICC_FACTORY_RESET =
|
private static final String ACTION_EUICC_FACTORY_RESET =
|
||||||
"com.android.internal.action.EUICC_FACTORY_RESET";
|
"com.android.internal.action.EUICC_FACTORY_RESET";
|
||||||
|
private static final String ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS =
|
||||||
|
"com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS";
|
||||||
|
|
||||||
/** used in {@link #wipeEuiccData} as package name of callback intent */
|
/**
|
||||||
private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android";
|
* Used in {@link #wipeEuiccData} & {@link #removeEuiccInvisibleSubs} as package name of
|
||||||
|
* callback intent.
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK = "android";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recovery image uses this file to identify the location (i.e. blocks)
|
* The recovery image uses this file to identify the location (i.e. blocks)
|
||||||
@@ -757,8 +769,11 @@ public class RecoverySystem {
|
|||||||
// Block until the ordered broadcast has completed.
|
// Block until the ordered broadcast has completed.
|
||||||
condition.block();
|
condition.block();
|
||||||
|
|
||||||
|
EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
|
||||||
if (wipeEuicc) {
|
if (wipeEuicc) {
|
||||||
wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
|
wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
|
||||||
|
} else {
|
||||||
|
removeEuiccInvisibleSubs(context, euiccManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
String shutdownArg = null;
|
String shutdownArg = null;
|
||||||
@@ -854,6 +869,110 @@ public class RecoverySystem {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void removeEuiccInvisibleSubs(
|
||||||
|
Context context, EuiccManager euiccManager) {
|
||||||
|
ContentResolver cr = context.getContentResolver();
|
||||||
|
if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
|
||||||
|
// If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles,
|
||||||
|
// as there's nothing to be removed.
|
||||||
|
Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned.");
|
||||||
|
return;
|
||||||
|
} else if (euiccManager == null || !euiccManager.isEnabled()) {
|
||||||
|
Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SubscriptionManager subscriptionManager =
|
||||||
|
context.getSystemService(SubscriptionManager.class);
|
||||||
|
List<SubscriptionInfo> availableSubs =
|
||||||
|
subscriptionManager.getAvailableSubscriptionInfoList();
|
||||||
|
if (availableSubs == null || availableSubs.isEmpty()) {
|
||||||
|
Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<SubscriptionInfo> invisibleSubs = new ArrayList<>();
|
||||||
|
for (SubscriptionInfo sub : availableSubs) {
|
||||||
|
if (sub.isEmbedded() && !subscriptionManager.isSubscriptionVisible(sub)) {
|
||||||
|
invisibleSubs.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean removeEuiccInvisibleSubs(
|
||||||
|
Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) {
|
||||||
|
if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
|
||||||
|
Log.i(TAG, "There are no eUICC invisible profiles needed to be removed.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size());
|
||||||
|
final AtomicInteger removedSubsCount = new AtomicInteger(0);
|
||||||
|
|
||||||
|
BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) {
|
||||||
|
if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
|
||||||
|
int detailedCode = intent.getIntExtra(
|
||||||
|
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
|
||||||
|
Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = "
|
||||||
|
+ detailedCode);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Successfully remove euicc opportunistic profile.");
|
||||||
|
removedSubsCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
removeSubsLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
|
||||||
|
intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
|
||||||
|
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
|
||||||
|
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
|
||||||
|
HandlerThread euiccHandlerThread =
|
||||||
|
new HandlerThread("euiccRemovingSubsReceiverThread");
|
||||||
|
euiccHandlerThread.start();
|
||||||
|
Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
|
||||||
|
context.getApplicationContext()
|
||||||
|
.registerReceiver(
|
||||||
|
removeEuiccSubsReceiver, intentFilter, null, euiccHandler);
|
||||||
|
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Remove invisible subscription " + subscriptionInfo.getSubscriptionId()
|
||||||
|
+ " from card " + subscriptionInfo.getCardId());
|
||||||
|
euiccManager.createForCardId(subscriptionInfo.getCardId())
|
||||||
|
.deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
long waitingTimeMillis = Settings.Global.getLong(
|
||||||
|
context.getContentResolver(),
|
||||||
|
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
|
||||||
|
DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS);
|
||||||
|
if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
|
||||||
|
waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
|
||||||
|
} else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
|
||||||
|
waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
|
||||||
|
}
|
||||||
|
if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
|
||||||
|
Log.e(TAG, "Timeout removing invisible euicc profiles.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
Log.e(TAG, "Removing invisible euicc profiles interrupted", e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver);
|
||||||
|
if (euiccHandlerThread != null) {
|
||||||
|
euiccHandlerThread.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedSubsCount.get() == subscriptionInfos.size();
|
||||||
|
}
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public static void rebootPromptAndWipeUserData(Context context, String reason)
|
public static void rebootPromptAndWipeUserData(Context context, String reason)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|||||||
@@ -13550,6 +13550,16 @@ public final class Settings {
|
|||||||
public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
|
public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
|
||||||
"location_settings_link_to_permissions_enabled";
|
"location_settings_link_to_permissions_enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to set the waiting time for removing invisible euicc profiles inside System >
|
||||||
|
* Settings.
|
||||||
|
* Type: long
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
|
||||||
|
"euicc_removing_invisible_profiles_timeout_millis";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to set the waiting time for euicc factory reset inside System > Settings
|
* Flag to set the waiting time for euicc factory reset inside System > Settings
|
||||||
* Type: long
|
* Type: long
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ public class SettingsBackupTest {
|
|||||||
Settings.Global.EUICC_PROVISIONED,
|
Settings.Global.EUICC_PROVISIONED,
|
||||||
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
|
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
|
||||||
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
|
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
|
||||||
|
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
|
||||||
Settings.Global.FANCY_IME_ANIMATIONS,
|
Settings.Global.FANCY_IME_ANIMATIONS,
|
||||||
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
|
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
|
||||||
Settings.Global.FORCED_APP_STANDBY_ENABLED,
|
Settings.Global.FORCED_APP_STANDBY_ENABLED,
|
||||||
|
|||||||
@@ -2976,10 +2976,10 @@ public class SubscriptionManager {
|
|||||||
* @param info the subscriptionInfo to check against.
|
* @param info the subscriptionInfo to check against.
|
||||||
* @return true if this subscription should be visible to the API caller.
|
* @return true if this subscription should be visible to the API caller.
|
||||||
*
|
*
|
||||||
|
* @hide
|
||||||
*/
|
*/
|
||||||
private boolean isSubscriptionVisible(SubscriptionInfo info) {
|
public boolean isSubscriptionVisible(SubscriptionInfo info) {
|
||||||
if (info == null) return false;
|
if (info == null) return false;
|
||||||
|
|
||||||
// If subscription is NOT grouped opportunistic subscription, it's visible.
|
// If subscription is NOT grouped opportunistic subscription, it's visible.
|
||||||
if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
|
if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user