Loading src/java/com/android/internal/telephony/CarrierResolver.java +7 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.metrics.CarrierIdMatchStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.TelephonyUtils; Loading Loading @@ -548,9 +549,14 @@ public class CarrierResolver extends Handler { // subscriptioninfo db to make sure we have correct carrier id set. if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) { // only persist carrier id to simInfo db when subId is valid. if (mPhone.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId); } else { SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId()); } } } private static CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) { String certs = cursor.getString( Loading src/java/com/android/internal/telephony/GsmCdmaPhone.java +13 −2 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.imsphone.ImsPhoneMmiCode; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.metrics.VoiceCallSessionStats; import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccCardStatus; Loading Loading @@ -349,8 +350,18 @@ public class GsmCdmaPhone extends Phone { mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null); if (isSubscriptionManagerServiceEnabled()) { mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback( this::post) { @Override public void onUiccApplicationsEnabled(int subId) { reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); } }); } else { SubscriptionController.getInstance().registerForUiccAppsEnabled(this, EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false); } mLinkBandwidthEstimator = mTelephonyComponentFactory .inject(LinkBandwidthEstimator.class.getName()) Loading src/java/com/android/internal/telephony/Phone.java +5 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final Context mContext; protected SubscriptionManagerService mSubscriptionManagerService; /** * PhoneNotifier is an abstraction for all system-wide * state change notification. DefaultPhoneNotifier is Loading Loading @@ -616,6 +618,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { mIsSubscriptionManagerServiceEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_using_subscription_manager_service); if (isSubscriptionManagerServiceEnabled()) { mSubscriptionManagerService = SubscriptionManagerService.getInstance(); } // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers. mTelephonyComponentFactory = telephonyComponentFactory; Loading src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java +217 −153 Original line number Diff line number Diff line Loading @@ -49,7 +49,9 @@ import com.android.telephony.Rlog; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; Loading Loading @@ -77,45 +79,6 @@ public class SubscriptionDatabaseManager extends Handler { /** Invalid database row index. */ private static final int INVALID_ROW_INDEX = -1; /** The context */ @NonNull private final Context mContext; /** The callback used for passing events back to {@link SubscriptionManagerService}. */ @NonNull private final SubscriptionDatabaseManagerCallback mCallback; /** Telephony manager */ private final TelephonyManager mTelephonyManager; /** UICC controller */ private final UiccController mUiccController; /** * The read/write lock to protect the entire database access. Using a Re-entrant read lock as * much more read requests are expected than the write requests. All the access to * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock. */ @NonNull private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** * The entire subscription database, including subscriptions from inserted, previously inserted * SIMs. This is the full memory cache of the subscription database. The key is the subscription * id. Note all the access to this map needs to be protected by the re-entrant lock * {@link #mReadWriteLock}. * * @see SimInfo */ @GuardedBy("mReadWriteLock") @NonNull private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache = new HashMap<>(16); /** The mapping from {@link SimInfo} table to {@link SubscriptionInfoInternal} get methods. */ private static final Map<String, Function<SubscriptionInfoInternal, ?>> SUBSCRIPTION_GET_METHOD_MAP = Map.ofEntries( Loading Loading @@ -265,6 +228,45 @@ public class SubscriptionDatabaseManager extends Handler { SubscriptionInfoInternal::getUserId) ); /** The context */ @NonNull private final Context mContext; /** The callback used for passing events back to {@link SubscriptionManagerService}. */ @NonNull private final SubscriptionDatabaseManagerCallback mCallback; /** UICC controller */ private final UiccController mUiccController; /** * The read/write lock to protect the entire database access. Using a Re-entrant read lock as * much more read requests are expected than the write requests. All the access to * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock. */ @NonNull private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** * The entire subscription database, including subscriptions from inserted, previously inserted * SIMs. This is the full memory cache of the subscription database. The key is the subscription * id. Note all the access to this map needs to be protected by the re-entrant lock * {@link #mReadWriteLock}. * * @see SimInfo */ @GuardedBy("mReadWriteLock") @NonNull private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache = new HashMap<>(16); /** Whether database has been loaded into the cache after boot up. */ private boolean mDatabaseLoaded = false; /** * This is the callback used for listening events from {@link SubscriptionDatabaseManager}. */ Loading Loading @@ -298,12 +300,25 @@ public class SubscriptionDatabaseManager extends Handler { mExecutor.execute(runnable); } /** * Called when database has been loaded into the cache. */ public abstract void onDatabaseLoaded(); /** * Called when subscription changed. * * @param subId The subscription id. */ public abstract void onSubscriptionChanged(int subId); /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} * changed. * * @param subId The subscription id. */ public abstract void onUiccApplicationsEnabled(int subId); } /** Loading @@ -318,7 +333,6 @@ public class SubscriptionDatabaseManager extends Handler { super(looper); mContext = context; mCallback = callback; mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mUiccController = UiccController.getInstance(); loadFromDatabase(); } Loading Loading @@ -414,6 +428,11 @@ public class SubscriptionDatabaseManager extends Handler { + "insert. subInfo=" + subInfo); } if (!mDatabaseLoaded) { throw new IllegalStateException("Database has not been loaded. Can't insert new " + "record at this point."); } int subId; // Grab the write lock so no other threads can read or write the cache. mReadWriteLock.writeLock().lock(); Loading Loading @@ -447,6 +466,12 @@ public class SubscriptionDatabaseManager extends Handler { private void updateDatabaseAsync(int subId, @NonNull ContentValues contentValues) { logv("updateDatabaseAsync: prepare to update sub " + subId); // Perform the update in the handler thread asynchronously. if (!mDatabaseLoaded) { throw new IllegalStateException("Database has not been loaded. Can't update " + "database at this point. contentValues=" + contentValues); } post(() -> { int rowsUpdated = mContext.getContentResolver().update(Uri.withAppendedPath( SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null); Loading Loading @@ -535,6 +560,10 @@ public class SubscriptionDatabaseManager extends Handler { if (oldSubInfo.equals(newSubInfo)) return; mAllSubscriptionInfoInternalCache.put(subId, newSubInfo); mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId)); if (oldSubInfo.areUiccApplicationsEnabled() != newSubInfo.areUiccApplicationsEnabled()) { mCallback.invokeFromExecutor(() -> mCallback.onUiccApplicationsEnabled(subId)); } // Writing into the database is slow. So do this asynchronously. updateDatabaseAsync(subId, createDeltaContentValues(oldSubInfo, newSubInfo)); } finally { Loading Loading @@ -1176,6 +1205,8 @@ public class SubscriptionDatabaseManager extends Handler { .put(subInfo.getSubscriptionId(), subInfo); } } mDatabaseLoaded = true; mCallback.invokeFromExecutor(mCallback::onDatabaseLoaded); log("Loaded " + mAllSubscriptionInfoInternalCache.size() + " records from the subscription database."); } finally { Loading Loading @@ -1311,7 +1342,7 @@ public class SubscriptionDatabaseManager extends Handler { /** * Get the subscription info by subscription id. * * @param subId The subscription id * @param subId The subscription id. * * @return The subscription info. {@code null} if not found. */ Loading @@ -1325,6 +1356,39 @@ public class SubscriptionDatabaseManager extends Handler { } } /** * @return All subscription infos in the database. */ @NonNull public List<SubscriptionInfoInternal> getAllSubscriptions() { return new ArrayList<>(mAllSubscriptionInfoInternalCache.values()); } /** * Get subscription info by ICCID. * * @param iccId The ICCID of the SIM card. * @return The subscription info if found. {@code null} if not found. */ @Nullable public SubscriptionInfoInternal getSubscriptionInfoInternalByIccId(@NonNull String iccId) { mReadWriteLock.readLock().lock(); try { return mAllSubscriptionInfoInternalCache.values().stream() .filter(subInfo -> subInfo.getIccId().equals(iccId)) .findFirst() .orElse(null); } finally { mReadWriteLock.readLock().unlock(); } } /** * @return {@code true} if the database has been loaded into the cache. */ public boolean isDatabaseLoaded() { return mDatabaseLoaded; } /** * Log debug messages. * Loading src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +241 −8 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.internal.telephony.subscription; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; Loading @@ -30,14 +34,19 @@ import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.SubscriptionManager.SubscriptionType; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.MultiSimSettingController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback; import com.android.internal.telephony.uicc.IccUtils; import com.android.telephony.Rlog; import java.io.FileDescriptor; Loading @@ -47,6 +56,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading @@ -58,18 +68,34 @@ public class SubscriptionManagerService extends ISub.Stub { /** Whether enabling verbose debugging message or not. */ private static final boolean VDBG = false; /** Instance of subscription manager service. */ @NonNull private static SubscriptionManagerService sInstance; /** The context */ @NonNull private final Context mContext; /** The main handler of subscription manager service. */ @NonNull private final Handler mHandler; /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** The subscription database manager. */ @NonNull private final SubscriptionDatabaseManager mSubscriptionDatabaseManager; @NonNull private final WatchedSlotIndexToSubId mSlotIndexToSubId = new WatchedSlotIndexToSubId(); /** Subscription manager service callbacks. */ @NonNull private final Set<SubscriptionManagerServiceCallback> mSubscriptionManagerServiceCallbacks = new ArraySet<>(); /** * Watched slot index to sub id map. */ Loading Loading @@ -142,7 +168,55 @@ public class SubscriptionManagerService extends ISub.Stub { } } private final WatchedSlotIndexToSubId mSlotIndexToSubId = new WatchedSlotIndexToSubId(); /** * This is the callback used for listening events from {@link SubscriptionManagerService}. */ public static class SubscriptionManagerServiceCallback { /** The executor of the callback. */ @NonNull private final Executor mExecutor; /** * Constructor * * @param executor The executor of the callback. */ public SubscriptionManagerServiceCallback(@NonNull @CallbackExecutor Executor executor) { mExecutor = executor; } /** * @return The executor of the callback. */ @NonNull @VisibleForTesting public Executor getExecutor() { return mExecutor; } /** * Invoke the callback from executor. * * @param runnable The callback method to invoke. */ public void invokeFromExecutor(@NonNull Runnable runnable) { mExecutor.execute(runnable); } /** * Called when subscription changed. * * @param subId The subscription id. */ public void onSubscriptionChanged(int subId) {} /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} changed. * * @param subId The subscription id. */ public void onUiccApplicationsEnabled(int subId) {} } /** * The constructor Loading @@ -151,13 +225,16 @@ public class SubscriptionManagerService extends ISub.Stub { * @param looper The looper for the handler. */ public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper) { sInstance = this; mContext = context; mHandler = new Handler(looper); TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer = TelephonyFrameworkInitializer .getTelephonyServiceManager() .getSubscriptionServiceRegisterer(); if (subscriptionServiceRegisterer.get() == null) { subscriptionServiceRegisterer.register(this); } // Create a separate thread for subscription database manager. The database will be updated // from a different thread. Loading @@ -165,6 +242,14 @@ public class SubscriptionManagerService extends ISub.Stub { handlerThread.start(); mSubscriptionDatabaseManager = new SubscriptionDatabaseManager(context, handlerThread.getLooper(), new SubscriptionDatabaseManagerCallback(mHandler::post) { /** * Called when database has been loaded into the cache. */ @Override public void onDatabaseLoaded() { log("Subscription database has been loaded."); } /** * Called when subscription changed. * Loading @@ -172,6 +257,10 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Override public void onSubscriptionChanged(int subId) { mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onSubscriptionChanged(subId))); MultiSimSettingController.getInstance().notifySubscriptionInfoChanged(); TelephonyRegistryManager telephonyRegistryManager = Loading @@ -190,9 +279,43 @@ public class SubscriptionManagerService extends ISub.Stub { // TODO: Call TelephonyMetrics.updateActiveSubscriptionInfoList when active // subscription changes. } /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} * changed. * * @param subId The subscription id. */ @Override public void onUiccApplicationsEnabled(int subId) { log("onUiccApplicationsEnabled: subId=" + subId); mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onUiccApplicationsEnabled(subId))); } }); } /** * @return The singleton instance of {@link SubscriptionManagerService}. */ @NonNull public static SubscriptionManagerService getInstance() { return sInstance; } /** * Set the subscription carrier id. * * @param subId Subscription id. * @param carrierId The carrier id. * * @see TelephonyManager#getSimCarrierId() */ public void setCarrierId(int subId, int carrierId) { mSubscriptionDatabaseManager.setCarrierId(subId, carrierId); } /** * @param callingPackage The package making the call. * @param callingFeatureId The feature in the package Loading Loading @@ -330,20 +453,70 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Add a new subscription info record, if needed. * Add a new subscription info record, if needed. This should be only used for remote SIM. * * @param uniqueId This is the unique identifier for the subscription within the specific * subscription type * @param displayName human-readable name of the device the subscription corresponds to * @param slotIndex the slot assigned to this device * @param iccId ICCID of the SIM card. * @param displayName human-readable name of the device the subscription corresponds to. * @param slotIndex the logical SIM slot index assigned to this device. * @param subscriptionType the type of subscription to be added * * @return 0 if success, < 0 on error */ @Override public int addSubInfo(@NonNull String uniqueId, @NonNull String displayName, int slotIndex, public int addSubInfo(@NonNull String iccId, @NonNull String displayName, int slotIndex, @SubscriptionType int subscriptionType) { log("addSubInfo: iccId=" + SubscriptionInfo.givePrintableIccid(iccId) + ", slotIndex=" + slotIndex + ", displayName=" + displayName + ", type=" + SubscriptionManager.subscriptionTypeToString(subscriptionType)); enforceModifyPhoneState("addSubInfo"); // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { if (TextUtils.isEmpty(iccId)) { loge("addSubInfo: null or empty iccId"); return -1; } if (subscriptionType != SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM || !mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE)) { loge("addSubInfo: remote SIM is only supported when FEATURE_AUTOMOTIVE is " + "enabled."); return -1; } if (slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) { loge("addSubInfo: This API can only be used for remote SIM. slotIndex=" + slotIndex); return -1; } iccId = IccUtils.stripTrailingFs(iccId); SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(iccId); // Check if the record exists or not. if (subInfo == null) { // Record does not exist. mSubscriptionDatabaseManager.insertSubscriptionInfo( new SubscriptionInfoInternal.Builder() .setIccId(iccId) .setSimSlotIndex(slotIndex) .setDisplayName(displayName) .setType(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM) .build() ); } else { // Record already exists. loge("Subscription record already existed."); return -1; } } finally { Binder.restoreCallingIdentity(identity); } return 0; } /** Loading Loading @@ -700,6 +873,66 @@ public class SubscriptionManagerService extends ISub.Stub { return null; } /** * Register the callback for receiving information from {@link SubscriptionManagerService}. * * @param callback The callback. */ public void registerCallback(@NonNull SubscriptionManagerServiceCallback callback) { mSubscriptionManagerServiceCallbacks.add(callback); } /** * Unregister the previously registered {@link SubscriptionManagerServiceCallback}. * * @param callback The callback to unregister. */ public void unregisterCallback(@NonNull SubscriptionManagerServiceCallback callback) { mSubscriptionManagerServiceCallbacks.remove(callback); } /** * Enforce {@link android.Manifest.permission#MODIFY_PHONE_STATE} permission * * @param message Error message included in the exception. */ private void enforceModifyPhoneState(String message) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE, message); } /** * Enforce {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} permission * * @param message Error message included in the exception. */ private void enforceReadPrivilegedPhoneState(String message) { mContext.enforceCallingOrSelfPermission( Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message); } /** * Enforce {@link android.Manifest.permission#MANAGE_SUBSCRIPTION_USER_ASSOCIATION} permission * * @param message Error message included in the exception. */ private void enforceManageSubscriptionUserAssociation(String message) { mContext.enforceCallingOrSelfPermission( Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION, message); } /** * Get the subscription info by subscription id. * * @param subId The subscription id. * * @return The subscription info. {@code null} if not found. */ @Nullable public SubscriptionInfoInternal getSubscriptionInfoInternal(int subId) { return mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId); } /** * Log debug messages. * Loading Loading
src/java/com/android/internal/telephony/CarrierResolver.java +7 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.metrics.CarrierIdMatchStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.util.TelephonyUtils; Loading Loading @@ -548,9 +549,14 @@ public class CarrierResolver extends Handler { // subscriptioninfo db to make sure we have correct carrier id set. if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) { // only persist carrier id to simInfo db when subId is valid. if (mPhone.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId); } else { SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId()); } } } private static CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) { String certs = cursor.getString( Loading
src/java/com/android/internal/telephony/GsmCdmaPhone.java +13 −2 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.imsphone.ImsPhoneMmiCode; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.metrics.VoiceCallSessionStats; import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccCardStatus; Loading Loading @@ -349,8 +350,18 @@ public class GsmCdmaPhone extends Phone { mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null); if (isSubscriptionManagerServiceEnabled()) { mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback( this::post) { @Override public void onUiccApplicationsEnabled(int subId) { reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); } }); } else { SubscriptionController.getInstance().registerForUiccAppsEnabled(this, EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false); } mLinkBandwidthEstimator = mTelephonyComponentFactory .inject(LinkBandwidthEstimator.class.getName()) Loading
src/java/com/android/internal/telephony/Phone.java +5 −0 Original line number Diff line number Diff line Loading @@ -444,6 +444,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final Context mContext; protected SubscriptionManagerService mSubscriptionManagerService; /** * PhoneNotifier is an abstraction for all system-wide * state change notification. DefaultPhoneNotifier is Loading Loading @@ -616,6 +618,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { mIsSubscriptionManagerServiceEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_using_subscription_manager_service); if (isSubscriptionManagerServiceEnabled()) { mSubscriptionManagerService = SubscriptionManagerService.getInstance(); } // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers. mTelephonyComponentFactory = telephonyComponentFactory; Loading
src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java +217 −153 Original line number Diff line number Diff line Loading @@ -49,7 +49,9 @@ import com.android.telephony.Rlog; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; Loading Loading @@ -77,45 +79,6 @@ public class SubscriptionDatabaseManager extends Handler { /** Invalid database row index. */ private static final int INVALID_ROW_INDEX = -1; /** The context */ @NonNull private final Context mContext; /** The callback used for passing events back to {@link SubscriptionManagerService}. */ @NonNull private final SubscriptionDatabaseManagerCallback mCallback; /** Telephony manager */ private final TelephonyManager mTelephonyManager; /** UICC controller */ private final UiccController mUiccController; /** * The read/write lock to protect the entire database access. Using a Re-entrant read lock as * much more read requests are expected than the write requests. All the access to * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock. */ @NonNull private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** * The entire subscription database, including subscriptions from inserted, previously inserted * SIMs. This is the full memory cache of the subscription database. The key is the subscription * id. Note all the access to this map needs to be protected by the re-entrant lock * {@link #mReadWriteLock}. * * @see SimInfo */ @GuardedBy("mReadWriteLock") @NonNull private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache = new HashMap<>(16); /** The mapping from {@link SimInfo} table to {@link SubscriptionInfoInternal} get methods. */ private static final Map<String, Function<SubscriptionInfoInternal, ?>> SUBSCRIPTION_GET_METHOD_MAP = Map.ofEntries( Loading Loading @@ -265,6 +228,45 @@ public class SubscriptionDatabaseManager extends Handler { SubscriptionInfoInternal::getUserId) ); /** The context */ @NonNull private final Context mContext; /** The callback used for passing events back to {@link SubscriptionManagerService}. */ @NonNull private final SubscriptionDatabaseManagerCallback mCallback; /** UICC controller */ private final UiccController mUiccController; /** * The read/write lock to protect the entire database access. Using a Re-entrant read lock as * much more read requests are expected than the write requests. All the access to * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock. */ @NonNull private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** * The entire subscription database, including subscriptions from inserted, previously inserted * SIMs. This is the full memory cache of the subscription database. The key is the subscription * id. Note all the access to this map needs to be protected by the re-entrant lock * {@link #mReadWriteLock}. * * @see SimInfo */ @GuardedBy("mReadWriteLock") @NonNull private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache = new HashMap<>(16); /** Whether database has been loaded into the cache after boot up. */ private boolean mDatabaseLoaded = false; /** * This is the callback used for listening events from {@link SubscriptionDatabaseManager}. */ Loading Loading @@ -298,12 +300,25 @@ public class SubscriptionDatabaseManager extends Handler { mExecutor.execute(runnable); } /** * Called when database has been loaded into the cache. */ public abstract void onDatabaseLoaded(); /** * Called when subscription changed. * * @param subId The subscription id. */ public abstract void onSubscriptionChanged(int subId); /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} * changed. * * @param subId The subscription id. */ public abstract void onUiccApplicationsEnabled(int subId); } /** Loading @@ -318,7 +333,6 @@ public class SubscriptionDatabaseManager extends Handler { super(looper); mContext = context; mCallback = callback; mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mUiccController = UiccController.getInstance(); loadFromDatabase(); } Loading Loading @@ -414,6 +428,11 @@ public class SubscriptionDatabaseManager extends Handler { + "insert. subInfo=" + subInfo); } if (!mDatabaseLoaded) { throw new IllegalStateException("Database has not been loaded. Can't insert new " + "record at this point."); } int subId; // Grab the write lock so no other threads can read or write the cache. mReadWriteLock.writeLock().lock(); Loading Loading @@ -447,6 +466,12 @@ public class SubscriptionDatabaseManager extends Handler { private void updateDatabaseAsync(int subId, @NonNull ContentValues contentValues) { logv("updateDatabaseAsync: prepare to update sub " + subId); // Perform the update in the handler thread asynchronously. if (!mDatabaseLoaded) { throw new IllegalStateException("Database has not been loaded. Can't update " + "database at this point. contentValues=" + contentValues); } post(() -> { int rowsUpdated = mContext.getContentResolver().update(Uri.withAppendedPath( SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null); Loading Loading @@ -535,6 +560,10 @@ public class SubscriptionDatabaseManager extends Handler { if (oldSubInfo.equals(newSubInfo)) return; mAllSubscriptionInfoInternalCache.put(subId, newSubInfo); mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId)); if (oldSubInfo.areUiccApplicationsEnabled() != newSubInfo.areUiccApplicationsEnabled()) { mCallback.invokeFromExecutor(() -> mCallback.onUiccApplicationsEnabled(subId)); } // Writing into the database is slow. So do this asynchronously. updateDatabaseAsync(subId, createDeltaContentValues(oldSubInfo, newSubInfo)); } finally { Loading Loading @@ -1176,6 +1205,8 @@ public class SubscriptionDatabaseManager extends Handler { .put(subInfo.getSubscriptionId(), subInfo); } } mDatabaseLoaded = true; mCallback.invokeFromExecutor(mCallback::onDatabaseLoaded); log("Loaded " + mAllSubscriptionInfoInternalCache.size() + " records from the subscription database."); } finally { Loading Loading @@ -1311,7 +1342,7 @@ public class SubscriptionDatabaseManager extends Handler { /** * Get the subscription info by subscription id. * * @param subId The subscription id * @param subId The subscription id. * * @return The subscription info. {@code null} if not found. */ Loading @@ -1325,6 +1356,39 @@ public class SubscriptionDatabaseManager extends Handler { } } /** * @return All subscription infos in the database. */ @NonNull public List<SubscriptionInfoInternal> getAllSubscriptions() { return new ArrayList<>(mAllSubscriptionInfoInternalCache.values()); } /** * Get subscription info by ICCID. * * @param iccId The ICCID of the SIM card. * @return The subscription info if found. {@code null} if not found. */ @Nullable public SubscriptionInfoInternal getSubscriptionInfoInternalByIccId(@NonNull String iccId) { mReadWriteLock.readLock().lock(); try { return mAllSubscriptionInfoInternalCache.values().stream() .filter(subInfo -> subInfo.getIccId().equals(iccId)) .findFirst() .orElse(null); } finally { mReadWriteLock.readLock().unlock(); } } /** * @return {@code true} if the database has been loaded into the cache. */ public boolean isDatabaseLoaded() { return mDatabaseLoaded; } /** * Log debug messages. * Loading
src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +241 −8 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.internal.telephony.subscription; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; Loading @@ -30,14 +34,19 @@ import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.SubscriptionManager.SubscriptionType; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.MultiSimSettingController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback; import com.android.internal.telephony.uicc.IccUtils; import com.android.telephony.Rlog; import java.io.FileDescriptor; Loading @@ -47,6 +56,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading @@ -58,18 +68,34 @@ public class SubscriptionManagerService extends ISub.Stub { /** Whether enabling verbose debugging message or not. */ private static final boolean VDBG = false; /** Instance of subscription manager service. */ @NonNull private static SubscriptionManagerService sInstance; /** The context */ @NonNull private final Context mContext; /** The main handler of subscription manager service. */ @NonNull private final Handler mHandler; /** Local log for most important debug messages. */ @NonNull private final LocalLog mLocalLog = new LocalLog(128); /** The subscription database manager. */ @NonNull private final SubscriptionDatabaseManager mSubscriptionDatabaseManager; @NonNull private final WatchedSlotIndexToSubId mSlotIndexToSubId = new WatchedSlotIndexToSubId(); /** Subscription manager service callbacks. */ @NonNull private final Set<SubscriptionManagerServiceCallback> mSubscriptionManagerServiceCallbacks = new ArraySet<>(); /** * Watched slot index to sub id map. */ Loading Loading @@ -142,7 +168,55 @@ public class SubscriptionManagerService extends ISub.Stub { } } private final WatchedSlotIndexToSubId mSlotIndexToSubId = new WatchedSlotIndexToSubId(); /** * This is the callback used for listening events from {@link SubscriptionManagerService}. */ public static class SubscriptionManagerServiceCallback { /** The executor of the callback. */ @NonNull private final Executor mExecutor; /** * Constructor * * @param executor The executor of the callback. */ public SubscriptionManagerServiceCallback(@NonNull @CallbackExecutor Executor executor) { mExecutor = executor; } /** * @return The executor of the callback. */ @NonNull @VisibleForTesting public Executor getExecutor() { return mExecutor; } /** * Invoke the callback from executor. * * @param runnable The callback method to invoke. */ public void invokeFromExecutor(@NonNull Runnable runnable) { mExecutor.execute(runnable); } /** * Called when subscription changed. * * @param subId The subscription id. */ public void onSubscriptionChanged(int subId) {} /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} changed. * * @param subId The subscription id. */ public void onUiccApplicationsEnabled(int subId) {} } /** * The constructor Loading @@ -151,13 +225,16 @@ public class SubscriptionManagerService extends ISub.Stub { * @param looper The looper for the handler. */ public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper) { sInstance = this; mContext = context; mHandler = new Handler(looper); TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer = TelephonyFrameworkInitializer .getTelephonyServiceManager() .getSubscriptionServiceRegisterer(); if (subscriptionServiceRegisterer.get() == null) { subscriptionServiceRegisterer.register(this); } // Create a separate thread for subscription database manager. The database will be updated // from a different thread. Loading @@ -165,6 +242,14 @@ public class SubscriptionManagerService extends ISub.Stub { handlerThread.start(); mSubscriptionDatabaseManager = new SubscriptionDatabaseManager(context, handlerThread.getLooper(), new SubscriptionDatabaseManagerCallback(mHandler::post) { /** * Called when database has been loaded into the cache. */ @Override public void onDatabaseLoaded() { log("Subscription database has been loaded."); } /** * Called when subscription changed. * Loading @@ -172,6 +257,10 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Override public void onSubscriptionChanged(int subId) { mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onSubscriptionChanged(subId))); MultiSimSettingController.getInstance().notifySubscriptionInfoChanged(); TelephonyRegistryManager telephonyRegistryManager = Loading @@ -190,9 +279,43 @@ public class SubscriptionManagerService extends ISub.Stub { // TODO: Call TelephonyMetrics.updateActiveSubscriptionInfoList when active // subscription changes. } /** * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} * changed. * * @param subId The subscription id. */ @Override public void onUiccApplicationsEnabled(int subId) { log("onUiccApplicationsEnabled: subId=" + subId); mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onUiccApplicationsEnabled(subId))); } }); } /** * @return The singleton instance of {@link SubscriptionManagerService}. */ @NonNull public static SubscriptionManagerService getInstance() { return sInstance; } /** * Set the subscription carrier id. * * @param subId Subscription id. * @param carrierId The carrier id. * * @see TelephonyManager#getSimCarrierId() */ public void setCarrierId(int subId, int carrierId) { mSubscriptionDatabaseManager.setCarrierId(subId, carrierId); } /** * @param callingPackage The package making the call. * @param callingFeatureId The feature in the package Loading Loading @@ -330,20 +453,70 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Add a new subscription info record, if needed. * Add a new subscription info record, if needed. This should be only used for remote SIM. * * @param uniqueId This is the unique identifier for the subscription within the specific * subscription type * @param displayName human-readable name of the device the subscription corresponds to * @param slotIndex the slot assigned to this device * @param iccId ICCID of the SIM card. * @param displayName human-readable name of the device the subscription corresponds to. * @param slotIndex the logical SIM slot index assigned to this device. * @param subscriptionType the type of subscription to be added * * @return 0 if success, < 0 on error */ @Override public int addSubInfo(@NonNull String uniqueId, @NonNull String displayName, int slotIndex, public int addSubInfo(@NonNull String iccId, @NonNull String displayName, int slotIndex, @SubscriptionType int subscriptionType) { log("addSubInfo: iccId=" + SubscriptionInfo.givePrintableIccid(iccId) + ", slotIndex=" + slotIndex + ", displayName=" + displayName + ", type=" + SubscriptionManager.subscriptionTypeToString(subscriptionType)); enforceModifyPhoneState("addSubInfo"); // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { if (TextUtils.isEmpty(iccId)) { loge("addSubInfo: null or empty iccId"); return -1; } if (subscriptionType != SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM || !mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE)) { loge("addSubInfo: remote SIM is only supported when FEATURE_AUTOMOTIVE is " + "enabled."); return -1; } if (slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) { loge("addSubInfo: This API can only be used for remote SIM. slotIndex=" + slotIndex); return -1; } iccId = IccUtils.stripTrailingFs(iccId); SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(iccId); // Check if the record exists or not. if (subInfo == null) { // Record does not exist. mSubscriptionDatabaseManager.insertSubscriptionInfo( new SubscriptionInfoInternal.Builder() .setIccId(iccId) .setSimSlotIndex(slotIndex) .setDisplayName(displayName) .setType(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM) .build() ); } else { // Record already exists. loge("Subscription record already existed."); return -1; } } finally { Binder.restoreCallingIdentity(identity); } return 0; } /** Loading Loading @@ -700,6 +873,66 @@ public class SubscriptionManagerService extends ISub.Stub { return null; } /** * Register the callback for receiving information from {@link SubscriptionManagerService}. * * @param callback The callback. */ public void registerCallback(@NonNull SubscriptionManagerServiceCallback callback) { mSubscriptionManagerServiceCallbacks.add(callback); } /** * Unregister the previously registered {@link SubscriptionManagerServiceCallback}. * * @param callback The callback to unregister. */ public void unregisterCallback(@NonNull SubscriptionManagerServiceCallback callback) { mSubscriptionManagerServiceCallbacks.remove(callback); } /** * Enforce {@link android.Manifest.permission#MODIFY_PHONE_STATE} permission * * @param message Error message included in the exception. */ private void enforceModifyPhoneState(String message) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE, message); } /** * Enforce {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} permission * * @param message Error message included in the exception. */ private void enforceReadPrivilegedPhoneState(String message) { mContext.enforceCallingOrSelfPermission( Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message); } /** * Enforce {@link android.Manifest.permission#MANAGE_SUBSCRIPTION_USER_ASSOCIATION} permission * * @param message Error message included in the exception. */ private void enforceManageSubscriptionUserAssociation(String message) { mContext.enforceCallingOrSelfPermission( Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION, message); } /** * Get the subscription info by subscription id. * * @param subId The subscription id. * * @return The subscription info. {@code null} if not found. */ @Nullable public SubscriptionInfoInternal getSubscriptionInfoInternal(int subId) { return mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId); } /** * Log debug messages. * Loading