blob: 124437cd6bf4c9a3c2d9803479a11e2b8a326e83 [file] [log] [blame]
/*
* Copyright 2022 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.internal.telephony.subscription;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.provider.Telephony;
import android.provider.Telephony.SimInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.DataRoamingMode;
import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
import android.telephony.SubscriptionManager.ProfileClass;
import android.telephony.SubscriptionManager.SimDisplayNameSource;
import android.telephony.SubscriptionManager.SubscriptionType;
import android.telephony.SubscriptionManager.UsageSetting;
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.ims.ImsMmTelManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.function.TriConsumer;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* The subscription database manager is the wrapper of {@link SimInfo}
* table. It's a full memory cache of the entire subscription database, and the update can be
* asynchronously or synchronously. The database's cache allows multi threads to read
* simultaneously, if no write is ongoing.
*
* Note that from Android 14, directly writing into the subscription database through content
* resolver with {@link SimInfo#CONTENT_URI} will cause cache/db out of sync. All the read/write
* to the database should go through {@link SubscriptionManagerService}.
*/
public class SubscriptionDatabaseManager extends Handler {
private static final String LOG_TAG = "SDMGR";
/** Whether enabling verbose debugging message or not. */
private static final boolean VDBG = false;
/** Invalid database row index. */
private static final int INVALID_ROW_INDEX = -1;
/** The mapping from {@link SimInfo} table to {@link SubscriptionInfoInternal} get methods. */
private static final Map<String, Function<SubscriptionInfoInternal, ?>>
SUBSCRIPTION_GET_METHOD_MAP = Map.ofEntries(
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
SubscriptionInfoInternal::getSubscriptionId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ICC_ID,
SubscriptionInfoInternal::getIccId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SIM_SLOT_INDEX,
SubscriptionInfoInternal::getSimSlotIndex),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_DISPLAY_NAME,
SubscriptionInfoInternal::getDisplayName),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARRIER_NAME,
SubscriptionInfoInternal::getCarrierName),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NAME_SOURCE,
SubscriptionInfoInternal::getDisplayNameSource),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_COLOR,
SubscriptionInfoInternal::getIconTint),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NUMBER,
SubscriptionInfoInternal::getNumber),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_DATA_ROAMING,
SubscriptionInfoInternal::getDataRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_MCC_STRING,
SubscriptionInfoInternal::getMcc),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_MNC_STRING,
SubscriptionInfoInternal::getMnc),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_EHPLMNS,
SubscriptionInfoInternal::getEhplmns),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_HPLMNS,
SubscriptionInfoInternal::getHplmns),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_EMBEDDED,
SubscriptionInfoInternal::getEmbedded),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARD_ID,
SubscriptionInfoInternal::getCardString),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ACCESS_RULES,
SubscriptionInfoInternal::getNativeAccessRules),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
SubscriptionInfoInternal::getCarrierConfigAccessRules),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_REMOVABLE,
SubscriptionInfoInternal::getRemovableEmbedded),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
SubscriptionInfoInternal::getCellBroadcastExtremeThreatAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
SubscriptionInfoInternal::getCellBroadcastSevereThreatAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_AMBER_ALERT,
SubscriptionInfoInternal::getCellBroadcastAmberAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_EMERGENCY_ALERT,
SubscriptionInfoInternal::getCellBroadcastEmergencyAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
SubscriptionInfoInternal::getCellBroadcastAlertSoundDuration),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
SubscriptionInfoInternal::getCellBroadcastAlertReminderInterval),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_VIBRATE,
SubscriptionInfoInternal::getCellBroadcastAlertVibrationEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_SPEECH,
SubscriptionInfoInternal::getCellBroadcastAlertSpeechEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
SubscriptionInfoInternal::getCellBroadcastEtwsTestAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
SubscriptionInfoInternal::getCellBroadcastAreaInfoMessageEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_CMAS_TEST_ALERT,
SubscriptionInfoInternal::getCellBroadcastTestAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_OPT_OUT_DIALOG,
SubscriptionInfoInternal::getCellBroadcastOptOutDialogEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
SubscriptionInfoInternal::getEnhanced4GModeEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_VT_IMS_ENABLED,
SubscriptionInfoInternal::getVideoTelephonyEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ENABLED,
SubscriptionInfoInternal::getWifiCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_MODE,
SubscriptionInfoInternal::getWifiCallingMode),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
SubscriptionInfoInternal::getWifiCallingModeForRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
SubscriptionInfoInternal::getWifiCallingEnabledForRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_OPPORTUNISTIC,
SubscriptionInfoInternal::getOpportunistic),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_GROUP_UUID,
SubscriptionInfoInternal::getGroupUuid),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ISO_COUNTRY_CODE,
SubscriptionInfoInternal::getCountryIso),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARRIER_ID,
SubscriptionInfoInternal::getCarrierId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PROFILE_CLASS,
SubscriptionInfoInternal::getProfileClass),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SUBSCRIPTION_TYPE,
SubscriptionInfoInternal::getSubscriptionType),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_GROUP_OWNER,
SubscriptionInfoInternal::getGroupOwner),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
SubscriptionInfoInternal::getEnabledMobileDataPolicies),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IMSI,
SubscriptionInfoInternal::getImsi),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
SubscriptionInfoInternal::getUiccApplicationsEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
SubscriptionInfoInternal::getRcsUceEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
SubscriptionInfoInternal::getCrossSimCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_RCS_CONFIG,
SubscriptionInfoInternal::getRcsConfig),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
SubscriptionInfoInternal::getAllowedNetworkTypesForReasons),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_D2D_STATUS_SHARING,
SubscriptionInfoInternal::getDeviceToDeviceStatusSharingPreference),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
SubscriptionInfoInternal::getVoImsOptInEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
SubscriptionInfoInternal::getDeviceToDeviceStatusSharingContacts),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
SubscriptionInfoInternal::getNrAdvancedCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
SubscriptionInfoInternal::getNumberFromCarrier),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
SubscriptionInfoInternal::getNumberFromIms),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PORT_INDEX,
SubscriptionInfoInternal::getPortIndex),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_USAGE_SETTING,
SubscriptionInfoInternal::getUsageSetting),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_TP_MESSAGE_REF,
SubscriptionInfoInternal::getLastUsedTPMessageReference),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_USER_HANDLE,
SubscriptionInfoInternal::getUserId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SATELLITE_ENABLED,
SubscriptionInfoInternal::getSatelliteEnabled)
);
/**
* The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
* {@link SubscriptionDatabaseManager} setting integer methods.
*/
private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, Integer>>
SUBSCRIPTION_SET_INTEGER_METHOD_MAP = Map.ofEntries(
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SIM_SLOT_INDEX,
SubscriptionDatabaseManager::setSimSlotIndex),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NAME_SOURCE,
SubscriptionDatabaseManager::setDisplayNameSource),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_COLOR,
SubscriptionDatabaseManager::setIconTint),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_DATA_ROAMING,
SubscriptionDatabaseManager::setDataRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_EMBEDDED,
SubscriptionDatabaseManager::setEmbedded),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_REMOVABLE,
SubscriptionDatabaseManager::setRemovableEmbedded),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
SubscriptionDatabaseManager::setCellBroadcastExtremeThreatAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
SubscriptionDatabaseManager::setCellBroadcastSevereThreatAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_AMBER_ALERT,
SubscriptionDatabaseManager::setCellBroadcastAmberAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_EMERGENCY_ALERT,
SubscriptionDatabaseManager::setCellBroadcastEmergencyAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
SubscriptionDatabaseManager::setCellBroadcastAlertSoundDuration),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
SubscriptionDatabaseManager::setCellBroadcastAlertReminderInterval),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_VIBRATE,
SubscriptionDatabaseManager::setCellBroadcastAlertVibrationEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ALERT_SPEECH,
SubscriptionDatabaseManager::setCellBroadcastAlertSpeechEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
SubscriptionDatabaseManager::setCellBroadcastEtwsTestAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
SubscriptionDatabaseManager::setCellBroadcastAreaInfoMessageEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_CMAS_TEST_ALERT,
SubscriptionDatabaseManager::setCellBroadcastTestAlertEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CB_OPT_OUT_DIALOG,
SubscriptionDatabaseManager::setCellBroadcastOptOutDialogEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
SubscriptionDatabaseManager::setEnhanced4GModeEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_VT_IMS_ENABLED,
SubscriptionDatabaseManager::setVideoTelephonyEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ENABLED,
SubscriptionDatabaseManager::setWifiCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_MODE,
SubscriptionDatabaseManager::setWifiCallingMode),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
SubscriptionDatabaseManager::setWifiCallingModeForRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
SubscriptionDatabaseManager::setWifiCallingEnabledForRoaming),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_OPPORTUNISTIC,
SubscriptionDatabaseManager::setOpportunistic),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARRIER_ID,
SubscriptionDatabaseManager::setCarrierId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PROFILE_CLASS,
SubscriptionDatabaseManager::setProfileClass),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SUBSCRIPTION_TYPE,
SubscriptionDatabaseManager::setSubscriptionType),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
SubscriptionDatabaseManager::setUiccApplicationsEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
SubscriptionDatabaseManager::setRcsUceEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
SubscriptionDatabaseManager::setCrossSimCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_D2D_STATUS_SHARING,
SubscriptionDatabaseManager::setDeviceToDeviceStatusSharingPreference),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
SubscriptionDatabaseManager::setVoImsOptInEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
SubscriptionDatabaseManager::setNrAdvancedCallingEnabled),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PORT_INDEX,
SubscriptionDatabaseManager::setPortIndex),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_USAGE_SETTING,
SubscriptionDatabaseManager::setUsageSetting),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_TP_MESSAGE_REF,
SubscriptionDatabaseManager::setLastUsedTPMessageReference),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_USER_HANDLE,
SubscriptionDatabaseManager::setUserId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SATELLITE_ENABLED,
SubscriptionDatabaseManager::setSatelliteEnabled)
);
/**
* The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
* {@link SubscriptionDatabaseManager} setting string methods.
*/
private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, String>>
SUBSCRIPTION_SET_STRING_METHOD_MAP = Map.ofEntries(
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ICC_ID,
SubscriptionDatabaseManager::setIccId),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_DISPLAY_NAME,
SubscriptionDatabaseManager::setDisplayName),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARRIER_NAME,
SubscriptionDatabaseManager::setCarrierName),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_NUMBER,
SubscriptionDatabaseManager::setNumber),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_MCC_STRING,
SubscriptionDatabaseManager::setMcc),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_MNC_STRING,
SubscriptionDatabaseManager::setMnc),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_EHPLMNS,
SubscriptionDatabaseManager::setEhplmns),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_HPLMNS,
SubscriptionDatabaseManager::setHplmns),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_CARD_ID,
SubscriptionDatabaseManager::setCardString),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_GROUP_UUID,
SubscriptionDatabaseManager::setGroupUuid),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ISO_COUNTRY_CODE,
SubscriptionDatabaseManager::setCountryIso),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_GROUP_OWNER,
SubscriptionDatabaseManager::setGroupOwner),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
SubscriptionDatabaseManager::setEnabledMobileDataPolicies),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IMSI,
SubscriptionDatabaseManager::setImsi),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
SubscriptionDatabaseManager::setAllowedNetworkTypesForReasons),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
SubscriptionDatabaseManager::setDeviceToDeviceStatusSharingContacts),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
SubscriptionDatabaseManager::setNumberFromCarrier),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
SubscriptionDatabaseManager::setNumberFromIms)
);
/**
* The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
* {@link SubscriptionDatabaseManager} setting byte array methods.
*/
private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, byte[]>>
SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP = Map.ofEntries(
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ACCESS_RULES,
SubscriptionDatabaseManager::setNativeAccessRules),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
SubscriptionDatabaseManager::setCarrierConfigAccessRules),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_RCS_CONFIG,
SubscriptionDatabaseManager::setRcsConfig)
);
/**
* The columns that should be in-sync between the subscriptions in the same group. Changing
* the value in those fields will automatically apply to the rest of the subscriptions in the
* group.
*
* @see SubscriptionManager#getSubscriptionsInGroup(ParcelUuid)
*/
private static final Set<String> GROUP_SHARING_COLUMNS = Set.of(
SimInfo.COLUMN_DISPLAY_NAME,
SimInfo.COLUMN_NAME_SOURCE,
SimInfo.COLUMN_COLOR,
SimInfo.COLUMN_DATA_ROAMING,
SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
SimInfo.COLUMN_VT_IMS_ENABLED,
SimInfo.COLUMN_WFC_IMS_ENABLED,
SimInfo.COLUMN_WFC_IMS_MODE,
SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
SimInfo.COLUMN_RCS_CONFIG,
SimInfo.COLUMN_D2D_STATUS_SHARING,
SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
SimInfo.COLUMN_USER_HANDLE
);
/**
* The deprecated columns that do not have corresponding set methods in
* {@link SubscriptionDatabaseManager}.
*/
private static final Set<String> DEPRECATED_DATABASE_COLUMNS = Set.of(
SimInfo.COLUMN_DISPLAY_NUMBER_FORMAT,
SimInfo.COLUMN_MCC,
SimInfo.COLUMN_MNC,
SimInfo.COLUMN_SIM_PROVISIONING_STATUS,
SimInfo.COLUMN_IS_METERED,
SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES,
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES
);
/** 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();
/** Indicating whether access the database asynchronously or not. */
private final boolean mAsyncMode;
/** 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 initialized after boot up. */
@GuardedBy("this")
private boolean mDatabaseInitialized = false;
/**
* This is the callback used for listening events from {@link SubscriptionDatabaseManager}.
*/
public abstract static class SubscriptionDatabaseManagerCallback {
/** The executor of the callback. */
private final @NonNull Executor mExecutor;
/**
* Constructor
*
* @param executor The executor of the callback.
*/
public SubscriptionDatabaseManagerCallback(@NonNull @CallbackExecutor Executor executor) {
mExecutor = executor;
}
/**
* @return The executor of the callback.
*/
@VisibleForTesting
public @NonNull 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 database has been initialized.
*/
public abstract void onInitialized();
/**
* Called when subscription changed.
*
* @param subId The subscription id.
*/
public abstract void onSubscriptionChanged(int subId);
}
/**
* The constructor.
*
* @param context The context.
* @param looper Looper for the handler.
* @param callback Subscription database callback.
*/
public SubscriptionDatabaseManager(@NonNull Context context, @NonNull Looper looper,
@NonNull SubscriptionDatabaseManagerCallback callback) {
super(looper);
log("Created SubscriptionDatabaseManager.");
mContext = context;
mCallback = callback;
mUiccController = UiccController.getInstance();
mAsyncMode = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_subscription_database_async_update);
initializeDatabase();
}
/**
* Helper method to get specific field from {@link SubscriptionInfoInternal} by the database
* column name. {@link SubscriptionInfoInternal} represent one single record in the
* {@link SimInfo} table. So every column has a corresponding get method in
* {@link SubscriptionInfoInternal} (except for unused or deprecated columns).
*
* @param subInfo The subscription info.
* @param columnName The database column name.
*
* @return The corresponding value from {@link SubscriptionInfoInternal}.
*
* @throws IllegalArgumentException if {@code columnName} is invalid.
*
* @see android.provider.Telephony.SimInfo for all the columns.
*/
@NonNull
private static Object getSubscriptionInfoFieldByColumnName(
@NonNull SubscriptionInfoInternal subInfo, @NonNull String columnName) {
if (SUBSCRIPTION_GET_METHOD_MAP.containsKey(columnName)) {
return SUBSCRIPTION_GET_METHOD_MAP.get(columnName).apply(subInfo);
}
throw new IllegalArgumentException("Invalid column name " + columnName);
}
/**
* Get a specific field from the subscription database by {@code subId} and {@code columnName}.
*
* @param subId The subscription id.
* @param columnName The database column name.
*
* @return The value from subscription database.
*
* @throws IllegalArgumentException if {@code subId} or {@code columnName} is invalid.
*
* @see android.provider.Telephony.SimInfo for all the columns.
*/
@NonNull
public Object getSubscriptionProperty(int subId, @NonNull String columnName) {
SubscriptionInfoInternal subInfo = getSubscriptionInfoInternal(subId);
if (subInfo == null) {
throw new IllegalArgumentException("getSubscriptionProperty: Invalid subId " + subId
+ ", columnName=" + columnName);
}
return getSubscriptionInfoFieldByColumnName(subInfo, columnName);
}
/**
* Set a field in the subscription database. Note not all fields are supported.
*
* @param subId Subscription Id of Subscription.
* @param columnName Column name in the database. Note not all fields are supported.
* @param value Value to store in the database.
*
* @throws IllegalArgumentException if {@code subId} or {@code columnName} is invalid, or
* {@code value} cannot be converted to the corresponding database column format.
* @throws NumberFormatException if a string value cannot be converted to integer.
* @throws ClassCastException if {@code value} cannot be casted to the required type.
*
* @see android.provider.Telephony.SimInfo for all the columns.
*/
public void setSubscriptionProperty(int subId, @NonNull String columnName,
@NonNull Object value) {
if (SUBSCRIPTION_SET_INTEGER_METHOD_MAP.containsKey(columnName)) {
// For integer type columns, accepting both integer and string that can be converted to
// integer.
int intValue;
if (value instanceof String) {
intValue = Integer.parseInt((String) value);
} else if (value instanceof Integer) {
intValue = (int) value;
} else {
throw new ClassCastException("columnName=" + columnName + ", cannot cast "
+ value.getClass() + " to integer.");
}
SUBSCRIPTION_SET_INTEGER_METHOD_MAP.get(columnName).accept(this, subId, intValue);
} else if (SUBSCRIPTION_SET_STRING_METHOD_MAP.containsKey(columnName)) {
// For string type columns. Will throw exception if value is not string type.
SUBSCRIPTION_SET_STRING_METHOD_MAP.get(columnName).accept(this, subId, (String) value);
} else if (SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP.containsKey(columnName)) {
// For byte array type columns, accepting both byte[] and string that can be converted
// to byte[] using base 64 encoding/decoding.
byte[] byteArrayValue;
if (value instanceof String) {
byteArrayValue = Base64.decode((String) value, Base64.DEFAULT);
} else if (value instanceof byte[]) {
byteArrayValue = (byte[]) value;
} else {
throw new ClassCastException("columnName=" + columnName + ", cannot cast "
+ value.getClass() + " to byte[].");
}
SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP.get(columnName).accept(
this, subId, byteArrayValue);
} else {
throw new IllegalArgumentException("Does not support set " + columnName + ".");
}
}
/**
* Comparing the old/new {@link SubscriptionInfoInternal} and create content values for database
* update. If any field in the new subscription info is different from the old one, then each
* delta will be added into the {@link ContentValues}.
*
* @param oldSubInfo The old {@link SubscriptionInfoInternal}.
* @param newSubInfo The new {@link SubscriptionInfoInternal}.
*
* @return The delta content values for database update.
*/
@NonNull
private ContentValues createDeltaContentValues(@Nullable SubscriptionInfoInternal oldSubInfo,
@NonNull SubscriptionInfoInternal newSubInfo) {
ContentValues deltaContentValues = new ContentValues();
for (String columnName : Telephony.SimInfo.getAllColumns()) {
if (DEPRECATED_DATABASE_COLUMNS.contains(columnName)) continue;
// subId is generated by the database. Cannot be updated.
if (columnName.equals(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID)) continue;
Object newValue = getSubscriptionInfoFieldByColumnName(newSubInfo, columnName);
if (newValue != null) {
Object oldValue = null;
if (oldSubInfo != null) {
oldValue = getSubscriptionInfoFieldByColumnName(oldSubInfo, columnName);
}
// Some columns need special handling. We need to convert them into a format that
// is accepted by the database.
if (!Objects.equals(oldValue, newValue)) {
deltaContentValues.putObject(columnName, newValue);
}
}
}
return deltaContentValues;
}
/**
* Synchronously insert a new record into the database. This operation is synchronous because
* we need to convert the inserted row index into the subscription id.
*
* @param contentValues The fields of the subscription to be inserted into the database.
*
* @return The row index of the new record. {@link #INVALID_ROW_INDEX} if insertion failed.
*/
private int insertNewRecordIntoDatabaseSync(@NonNull ContentValues contentValues) {
Objects.requireNonNull(contentValues);
Uri uri = mContext.getContentResolver().insert(SimInfo.CONTENT_URI, contentValues);
if (uri != null && uri.getLastPathSegment() != null) {
int subId = Integer.parseInt(uri.getLastPathSegment());
if (SubscriptionManager.isValidSubscriptionId(subId)) {
logv("insertNewRecordIntoDatabaseSync: contentValues=" + contentValues);
logl("insertNewRecordIntoDatabaseSync: Successfully added subscription. subId="
+ uri.getLastPathSegment());
return subId;
}
}
logel("insertNewRecordIntoDatabaseSync: Failed to insert subscription into database. "
+ "contentValues=" + contentValues);
return INVALID_ROW_INDEX;
}
/**
* Insert a new subscription info. The subscription info must have subscription id
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. Note this is a slow method, so be
* cautious to call this method.
*
* @param subInfo The subscription info to update.
*
* @return The new subscription id. {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} (-1) if
* insertion fails.
*/
public int insertSubscriptionInfo(@NonNull SubscriptionInfoInternal subInfo) {
Objects.requireNonNull(subInfo);
// A new subscription to be inserted must have invalid subscription id.
if (SubscriptionManager.isValidSubscriptionId(subInfo.getSubscriptionId())) {
throw new RuntimeException("insertSubscriptionInfo: Not a new subscription to "
+ "insert. subInfo=" + subInfo);
}
synchronized (this) {
if (!mDatabaseInitialized) {
throw new IllegalStateException(
"Database has not been initialized. 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();
try {
// Synchronously insert into the database. Note this should be the only synchronous
// write operation performed by the subscription database manager. The reason is that
// we need to get the sub id for cache update.
subId = insertNewRecordIntoDatabaseSync(createDeltaContentValues(null, subInfo));
if (subId > 0) {
mAllSubscriptionInfoInternalCache.put(subId, new SubscriptionInfoInternal
.Builder(subInfo)
.setId(subId).build());
} else {
logel("insertSubscriptionInfo: Failed to insert a new subscription. subInfo="
+ subInfo);
}
} finally {
mReadWriteLock.writeLock().unlock();
}
mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
return subId;
}
/**
* Remove a subscription record from the database.
*
* @param subId The subscription id of the subscription to be deleted.
*
* @throws IllegalArgumentException If {@code subId} is invalid.
*/
public void removeSubscriptionInfo(int subId) {
if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) {
throw new IllegalArgumentException("subId " + subId + " is invalid.");
}
mReadWriteLock.writeLock().lock();
try {
if (mContext.getContentResolver().delete(SimInfo.CONTENT_URI,
SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
new String[]{Integer.toString(subId)}) > 0) {
mAllSubscriptionInfoInternalCache.remove(subId);
} else {
logel("Failed to remove subscription with subId=" + subId);
}
} finally {
mReadWriteLock.writeLock().unlock();
}
mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
}
/**
* Update a subscription in the database (synchronously or asynchronously).
*
* @param subId The subscription id of the subscription to be updated.
* @param contentValues The fields to be update.
*
* @return The number of rows updated. Note if the database is configured as asynchronously
* update, then this will be always 1.
*/
private int updateDatabase(int subId, @NonNull ContentValues contentValues) {
logv("updateDatabase: prepare to update sub " + subId);
synchronized (this) {
if (!mDatabaseInitialized) {
logel("updateDatabase: Database has not been initialized. Can't update database at "
+ "this point. contentValues=" + contentValues);
return 0;
}
}
if (mAsyncMode) {
// Perform the update in the handler thread asynchronously.
post(() -> {
mContext.getContentResolver().update(Uri.withAppendedPath(
SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null);
logv("updateDatabase: async updated subscription in the database."
+ " subId=" + subId + ", contentValues= " + contentValues.getValues());
});
return 1;
} else {
logv("updateDatabase: sync updated subscription in the database."
+ " subId=" + subId + ", contentValues= " + contentValues.getValues());
return mContext.getContentResolver().update(Uri.withAppendedPath(
SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null);
}
}
/**
* Update a certain field of subscription in the database. Also update the subscription cache
* {@link #mAllSubscriptionInfoInternalCache}.
*
* @param subId The subscription id.
* @param columnName The database column name from the database table {@link SimInfo}.
* @param newValue The new value to update the subscription info cache
* {@link #mAllSubscriptionInfoInternalCache}.
* @param builderSetMethod The {@link SubscriptionInfo.Builder} method to set a specific field
* when constructing the new {@link SubscriptionInfo}. This should be one of the
* SubscriptionInfoInternal.Builder.setXxxx method.
* @param <T> The type of newValue for subscription cache update.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
private <T> void writeDatabaseAndCacheHelper(int subId, @NonNull String columnName,
@Nullable T newValue,
BiFunction<SubscriptionInfoInternal.Builder, T, SubscriptionInfoInternal.Builder>
builderSetMethod) {
ContentValues contentValues = new ContentValues();
// Grab the write lock so no other threads can read or write the cache.
mReadWriteLock.writeLock().lock();
try {
final SubscriptionInfoInternal oldSubInfo =
mAllSubscriptionInfoInternalCache.get(subId);
if (oldSubInfo == null) {
logel("Subscription doesn't exist. subId=" + subId + ", columnName=" + columnName);
throw new IllegalArgumentException("Subscription doesn't exist. subId=" + subId
+ ", columnName=" + columnName);
}
// Check if writing this field should automatically write to the rest of subscriptions
// in the same group.
final boolean syncToGroup = GROUP_SHARING_COLUMNS.contains(columnName);
mAllSubscriptionInfoInternalCache.forEach((id, subInfo) -> {
if (id == subId || (syncToGroup && !oldSubInfo.getGroupUuid().isEmpty()
&& oldSubInfo.getGroupUuid().equals(subInfo.getGroupUuid()))) {
// Check if the new value is different from the old value in the cache.
if (!Objects.equals(getSubscriptionInfoFieldByColumnName(subInfo, columnName),
newValue)) {
logv("writeDatabaseAndCacheHelper: subId=" + subId + ",columnName="
+ columnName + ", newValue=" + newValue);
// If the value is different, then we need to update the cache. Since all
// fields in SubscriptionInfo are final, we need to create a new
// SubscriptionInfo.
SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal
.Builder(subInfo);
// Apply the new value to the builder. This line is equivalent to
// builder.setXxxxxx(newValue);
builder = builderSetMethod.apply(builder, newValue);
// Prepare the content value for update.
contentValues.putObject(columnName, newValue);
if (updateDatabase(id, contentValues) > 0) {
// Update the subscription database cache.
mAllSubscriptionInfoInternalCache.put(id, builder.build());
mCallback.invokeFromExecutor(()
-> mCallback.onSubscriptionChanged(subId));
}
}
}
});
} finally {
mReadWriteLock.writeLock().unlock();
}
}
/**
* Update the database with the {@link SubscriptionInfoInternal}, and also update the cache.
*
* @param newSubInfo The new {@link SubscriptionInfoInternal}.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void updateSubscription(@NonNull SubscriptionInfoInternal newSubInfo) {
Objects.requireNonNull(newSubInfo);
// Grab the write lock so no other threads can read or write the cache.
mReadWriteLock.writeLock().lock();
try {
int subId = newSubInfo.getSubscriptionId();
SubscriptionInfoInternal oldSubInfo = mAllSubscriptionInfoInternalCache.get(
newSubInfo.getSubscriptionId());
if (oldSubInfo == null) {
throw new IllegalArgumentException("updateSubscription: subscription does not "
+ "exist. subId=" + subId);
}
if (oldSubInfo.equals(newSubInfo)) return;
if (updateDatabase(subId, createDeltaContentValues(oldSubInfo, newSubInfo)) > 0) {
mAllSubscriptionInfoInternalCache.put(subId, newSubInfo);
mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
}
} finally {
mReadWriteLock.writeLock().unlock();
}
}
/**
* Set the ICCID of the SIM that is associated with the subscription.
*
* @param subId Subscription id.
* @param iccId The ICCID of the SIM that is associated with this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setIccId(int subId, @NonNull String iccId) {
Objects.requireNonNull(iccId);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ICC_ID, iccId,
SubscriptionInfoInternal.Builder::setIccId);
}
/**
* Set the SIM index of the slot that currently contains the subscription. Set to
* {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
*
* @param subId Subscription id.
* @param simSlotIndex The SIM slot index.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setSimSlotIndex(int subId, int simSlotIndex) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SIM_SLOT_INDEX, simSlotIndex,
SubscriptionInfoInternal.Builder::setSimSlotIndex);
}
/**
* Set the name displayed to the user that identifies this subscription. This name is used
* in Settings page and can be renamed by the user.
*
* @param subId Subscription id.
* @param displayName The display name.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setDisplayName(int subId, @NonNull String displayName) {
Objects.requireNonNull(displayName);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DISPLAY_NAME, displayName,
SubscriptionInfoInternal.Builder::setDisplayName);
}
/**
* Set the name displayed to the user that identifies subscription provider name. This name
* is the SPN displayed in status bar and many other places. Can't be renamed by the user.
*
* @param subId Subscription id.
* @param carrierName The carrier name.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCarrierName(int subId, @NonNull String carrierName) {
Objects.requireNonNull(carrierName);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_NAME, carrierName,
SubscriptionInfoInternal.Builder::setCarrierName);
}
/**
* Set the source of the display name.
*
* @param subId Subscription id.
* @param displayNameSource The source of the display name.
*
* @throws IllegalArgumentException if the subscription does not exist.
*
* @see SubscriptionInfo#getDisplayName()
*/
public void setDisplayNameSource(int subId, @SimDisplayNameSource int displayNameSource) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NAME_SOURCE, displayNameSource,
SubscriptionInfoInternal.Builder::setDisplayNameSource);
}
/**
* Set the color to be used for tinting the icon when displaying to the user.
*
* @param subId Subscription id.
* @param iconTint The color to be used for tinting the icon when displaying to the user.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setIconTint(int subId, @ColorInt int iconTint) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_COLOR, iconTint,
SubscriptionInfoInternal.Builder::setIconTint);
}
/**
* Set the number presented to the user identify this subscription.
*
* @param subId Subscription id.
* @param number the number presented to the user identify this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setNumber(int subId, @NonNull String number) {
Objects.requireNonNull(number);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NUMBER, number,
SubscriptionInfoInternal.Builder::setNumber);
}
/**
* Set whether user enables data roaming for this subscription or not.
*
* @param subId Subscription id.
* @param dataRoaming Data roaming mode. Either
* {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
* {@link SubscriptionManager#DATA_ROAMING_DISABLE}
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setDataRoaming(int subId, @DataRoamingMode int dataRoaming) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DATA_ROAMING, dataRoaming,
SubscriptionInfoInternal.Builder::setDataRoaming);
}
/**
* Set the mobile country code.
*
* @param subId Subscription id.
* @param mcc The mobile country code.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setMcc(int subId, @NonNull String mcc) {
Objects.requireNonNull(mcc);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MCC_STRING, mcc,
SubscriptionInfoInternal.Builder::setMcc);
}
/**
* Set the mobile network code.
*
* @param subId Subscription id.
* @param mnc Mobile network code.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setMnc(int subId, @NonNull String mnc) {
Objects.requireNonNull(mnc);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MNC_STRING, mnc,
SubscriptionInfoInternal.Builder::setMnc);
}
/**
* Set EHPLMNs associated with the subscription.
*
* @param subId Subscription id.
* @param ehplmns EHPLMNs associated with the subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEhplmns(int subId, @NonNull String[] ehplmns) {
Objects.requireNonNull(ehplmns);
setEhplmns(subId, Arrays.stream(ehplmns)
.filter(Predicate.not(TextUtils::isEmpty))
.collect(Collectors.joining(",")));
}
/**
* Set EHPLMNs associated with the subscription.
*
* @param subId Subscription id.
* @param ehplmns EHPLMNs associated with the subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEhplmns(int subId, @NonNull String ehplmns) {
Objects.requireNonNull(ehplmns);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_EHPLMNS, ehplmns,
SubscriptionInfoInternal.Builder::setEhplmns);
}
/**
* Set HPLMNs associated with the subscription.
*
* @param subId Subscription id.
* @param hplmns HPLMNs associated with the subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setHplmns(int subId, @NonNull String[] hplmns) {
Objects.requireNonNull(hplmns);
setHplmns(subId, Arrays.stream(hplmns)
.filter(Predicate.not(TextUtils::isEmpty))
.collect(Collectors.joining(",")));
}
/**
* Set HPLMNs associated with the subscription.
*
* @param subId Subscription id.
* @param hplmns HPLMNs associated with the subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setHplmns(int subId, @NonNull String hplmns) {
Objects.requireNonNull(hplmns);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_HPLMNS, hplmns,
SubscriptionInfoInternal.Builder::setHplmns);
}
/**
* Set whether the subscription is from eSIM or not.
*
* @param subId Subscription id.
* @param isEmbedded if the subscription is from eSIM.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEmbedded(int subId, int isEmbedded) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_EMBEDDED, isEmbedded,
SubscriptionInfoInternal.Builder::setEmbedded);
}
/**
* Set whether the subscription is from eSIM or not.
*
* @param subId Subscription id.
* @param isEmbedded {@code true} if the subscription is from eSIM.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEmbedded(int subId, boolean isEmbedded) {
setEmbedded(subId, isEmbedded ? 1 : 0);
}
/**
* Set the card string of the SIM card. This is usually the ICCID or EID.
*
* @param subId Subscription id.
* @param cardString The card string of the SIM card.
*
* @throws IllegalArgumentException if the subscription does not exist.
*
* @see SubscriptionInfo#getCardString()
*/
public void setCardString(int subId, @NonNull String cardString) {
Objects.requireNonNull(cardString);
// Also update the public card id.
setCardId(subId, mUiccController.convertToPublicCardId(cardString));
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARD_ID, cardString,
SubscriptionInfoInternal.Builder::setCardString);
}
/**
* Set the card id. This is the non-PII card id converted from
* {@link SubscriptionInfoInternal#getCardString()}. This field only exists in
* {@link SubscriptionInfo}, but not the database.
*
* @param subId Subscription id.
* @param cardId The card id.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCardId(int subId, int cardId) {
// card id does not have a corresponding SimInfo column. So we only update the cache.
// Grab the write lock so no other threads can read or write the cache.
mReadWriteLock.writeLock().lock();
try {
SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
if (subInfoCache == null) {
throw new IllegalArgumentException("setCardId: Subscription doesn't exist. subId="
+ subId);
}
mAllSubscriptionInfoInternalCache.put(subId,
new SubscriptionInfoInternal.Builder(subInfoCache)
.setCardId(cardId).build());
} finally {
mReadWriteLock.writeLock().unlock();
}
}
/**
* Set the native access rules for this subscription, if it is embedded and defines any.
* This does not include access rules for non-embedded subscriptions.
*
* @param subId Subscription id.
* @param nativeAccessRules The native access rules for this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setNativeAccessRules(int subId, @NonNull byte[] nativeAccessRules) {
Objects.requireNonNull(nativeAccessRules);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES, nativeAccessRules,
SubscriptionInfoInternal.Builder::setNativeAccessRules);
}
/**
* Set the carrier certificates for this subscription that are saved in carrier configs.
* This does not include access rules from the Uicc, whether embedded or non-embedded.
*
* @param subId Subscription id.
* @param carrierConfigAccessRules The carrier certificates for this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCarrierConfigAccessRules(int subId, @NonNull byte[] carrierConfigAccessRules) {
Objects.requireNonNull(carrierConfigAccessRules);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
carrierConfigAccessRules,
SubscriptionInfoInternal.Builder::setCarrierConfigAccessRules);
}
/**
* Set the carrier certificates for this subscription that are saved in carrier configs.
* This does not include access rules from the Uicc, whether embedded or non-embedded.
*
* @param subId Subscription id.
* @param carrierConfigAccessRules The carrier certificates for this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCarrierConfigAccessRules(int subId,
@NonNull UiccAccessRule[] carrierConfigAccessRules) {
Objects.requireNonNull(carrierConfigAccessRules);
byte[] carrierConfigAccessRulesBytes = UiccAccessRule.encodeRules(carrierConfigAccessRules);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
carrierConfigAccessRulesBytes,
SubscriptionInfoInternal.Builder::setCarrierConfigAccessRules);
}
/**
* Set whether an embedded subscription is on a removable card. Such subscriptions are
* marked inaccessible as soon as the current card is removed. Otherwise, they will remain
* accessible unless explicitly deleted. Only meaningful for embedded subscription.
*
* @param subId Subscription id.
* @param isRemovableEmbedded if the subscription is from the removable embedded SIM.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setRemovableEmbedded(int subId, int isRemovableEmbedded) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_REMOVABLE, isRemovableEmbedded,
SubscriptionInfoInternal.Builder::setRemovableEmbedded);
}
/**
* Set whether cell broadcast extreme threat alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isExtremeThreatAlertEnabled whether cell broadcast extreme threat alert is enabled by
* the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastExtremeThreatAlertEnabled(int subId,
int isExtremeThreatAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
isExtremeThreatAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastExtremeThreatAlertEnabled);
}
/**
* Set whether cell broadcast severe threat alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isSevereThreatAlertEnabled whether cell broadcast severe threat alert is enabled by
* the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastSevereThreatAlertEnabled(int subId,
int isSevereThreatAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
isSevereThreatAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastSevereThreatAlertEnabled);
}
/**
* Set whether cell broadcast amber alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isAmberAlertEnabled whether cell broadcast amber alert is enabled by
* the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAmberAlertEnabled(int subId, int isAmberAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_AMBER_ALERT, isAmberAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastAmberAlertEnabled);
}
/**
* Set whether cell broadcast emergency alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isEmergencyAlertEnabled whether cell broadcast emergency alert is enabled by
* the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastEmergencyAlertEnabled(int subId,
int isEmergencyAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_EMERGENCY_ALERT,
isEmergencyAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastEmergencyAlertEnabled);
}
/**
* Set cell broadcast alert sound duration.
*
* @param subId Subscription id.
* @param alertSoundDuration Alert sound duration in seconds.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAlertSoundDuration(int subId, int alertSoundDuration) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
alertSoundDuration,
SubscriptionInfoInternal.Builder::setCellBroadcastAlertSoundDuration);
}
/**
* Set cell broadcast alert reminder interval.
*
* @param subId Subscription id.
* @param reminderInterval Alert reminder interval in milliseconds.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAlertReminderInterval(int subId, int reminderInterval) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
reminderInterval,
SubscriptionInfoInternal.Builder::setCellBroadcastAlertReminderInterval);
}
/**
* Set whether cell broadcast alert vibration is enabled by the user or not.
*
* @param subId Subscription id.
* @param isAlertVibrationEnabled whether cell broadcast alert vibration is enabled by the user
* or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAlertVibrationEnabled(int subId, int isAlertVibrationEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_VIBRATE, isAlertVibrationEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastAlertVibrationEnabled);
}
/**
* Set whether cell broadcast alert speech is enabled by the user or not.
*
* @param subId Subscription id.
* @param isAlertSpeechEnabled whether cell broadcast alert speech is enabled by the user or
* not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAlertSpeechEnabled(int subId, int isAlertSpeechEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_SPEECH, isAlertSpeechEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastAlertSpeechEnabled);
}
/**
* Set whether ETWS test alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isEtwsTestAlertEnabled whether cell broadcast ETWS test alert is enabled by the user
* or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastEtwsTestAlertEnabled(int subId, int isEtwsTestAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
isEtwsTestAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastEtwsTestAlertEnabled);
}
/**
* Set whether area info message is enabled by the user or not.
*
* @param subId Subscription id.
* @param isAreaInfoMessageEnabled whether cell broadcast area info message is enabled by the
* user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastAreaInfoMessageEnabled(int subId, int isAreaInfoMessageEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
isAreaInfoMessageEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastAreaInfoMessageEnabled);
}
/**
* Set whether cell broadcast test alert is enabled by the user or not.
*
* @param subId Subscription id.
* @param isTestAlertEnabled whether cell broadcast test alert is enabled by the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastTestAlertEnabled(int subId, int isTestAlertEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_CMAS_TEST_ALERT, isTestAlertEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastTestAlertEnabled);
}
/**
* Set whether cell broadcast opt-out dialog should be shown or not.
*
* @param subId Subscription id.
* @param isOptOutDialogEnabled whether cell broadcast opt-out dialog should be shown or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCellBroadcastOptOutDialogEnabled(int subId, int isOptOutDialogEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_OPT_OUT_DIALOG, isOptOutDialogEnabled,
SubscriptionInfoInternal.Builder::setCellBroadcastOptOutDialogEnabled);
}
/**
* Set whether enhanced 4G mode is enabled by the user or not.
*
* @param subId Subscription id.
* @param isEnhanced4GModeEnabled whether enhanced 4G mode is enabled by the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEnhanced4GModeEnabled(int subId, int isEnhanced4GModeEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
isEnhanced4GModeEnabled,
SubscriptionInfoInternal.Builder::setEnhanced4GModeEnabled);
}
/**
* Set whether video telephony is enabled by the user or not.
*
* @param subId Subscription id.
* @param isVideoTelephonyEnabled whether video telephony is enabled by the user or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setVideoTelephonyEnabled(int subId, int isVideoTelephonyEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VT_IMS_ENABLED, isVideoTelephonyEnabled,
SubscriptionInfoInternal.Builder::setVideoTelephonyEnabled);
}
/**
* Set whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
*
* @param subId Subscription id.
* @param isWifiCallingEnabled whether Wi-Fi calling is enabled by the user or not when the
* device is not roaming.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setWifiCallingEnabled(int subId, int isWifiCallingEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ENABLED, isWifiCallingEnabled,
SubscriptionInfoInternal.Builder::setWifiCallingEnabled);
}
/**
* Set Wi-Fi calling mode when the device is not roaming.
*
* @param subId Subscription id.
* @param wifiCallingMode Wi-Fi calling mode when the device is not roaming.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setWifiCallingMode(int subId,
@ImsMmTelManager.WiFiCallingMode int wifiCallingMode) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_MODE, wifiCallingMode,
SubscriptionInfoInternal.Builder::setWifiCallingMode);
}
/**
* Set Wi-Fi calling mode when the device is roaming.
*
* @param subId Subscription id.
* @param wifiCallingModeForRoaming Wi-Fi calling mode when the device is roaming.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setWifiCallingModeForRoaming(int subId,
@ImsMmTelManager.WiFiCallingMode int wifiCallingModeForRoaming) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
wifiCallingModeForRoaming,
SubscriptionInfoInternal.Builder::setWifiCallingModeForRoaming);
}
/**
* Set whether Wi-Fi calling is enabled by the user or not when the device is roaming.
*
* @param subId Subscription id.
* @param isWifiCallingEnabledForRoaming whether Wi-Fi calling is enabled by the user or not
* when the device is roaming.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setWifiCallingEnabledForRoaming(int subId, int isWifiCallingEnabledForRoaming) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
isWifiCallingEnabledForRoaming,
SubscriptionInfoInternal.Builder::setWifiCallingEnabledForRoaming);
}
/**
* Set whether the subscription is opportunistic or not.
*
* @param subId Subscription id.
* @param isOpportunistic if the subscription is opportunistic.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setOpportunistic(int subId, boolean isOpportunistic) {
setOpportunistic(subId, isOpportunistic ? 1 : 0);
}
/**
* Set whether the subscription is opportunistic or not.
*
* @param subId Subscription id.
* @param isOpportunistic if the subscription is opportunistic.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setOpportunistic(int subId, int isOpportunistic) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_OPPORTUNISTIC, isOpportunistic,
SubscriptionInfoInternal.Builder::setOpportunistic);
}
/**
* Set the group UUID of the subscription group.
*
* @param subId Subscription id.
* @param groupUuid The group UUID.
*
* @throws IllegalArgumentException if the subscription does not exist.
*
* @see SubscriptionInfo#getGroupUuid()
*/
public void setGroupUuid(int subId, @NonNull String groupUuid) {
Objects.requireNonNull(groupUuid);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_UUID, groupUuid,
SubscriptionInfoInternal.Builder::setGroupUuid);
}
/**
* Set the ISO Country code for the subscription's provider.
*
* @param subId Subscription id.
* @param countryIso The ISO country code for the subscription's provider.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCountryIso(int subId, @NonNull String countryIso) {
Objects.requireNonNull(countryIso);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ISO_COUNTRY_CODE, countryIso,
SubscriptionInfoInternal.Builder::setCountryIso);
}
/**
* Set the subscription carrier id.
*
* @param subId Subscription id.
* @param carrierId The carrier id.
*
* @throws IllegalArgumentException if the subscription does not exist.
*
* @see TelephonyManager#getSimCarrierId()
*/
public void setCarrierId(int subId, int carrierId) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_ID, carrierId,
SubscriptionInfoInternal.Builder::setCarrierId);
}
/**
* Set the profile class populated from the profile metadata if present.
*
* @param subId Subscription id.
* @param profileClass the profile class populated from the profile metadata if present.
*
* @throws IllegalArgumentException if the subscription does not exist.
*
* @see SubscriptionInfo#getProfileClass()
*/
public void setProfileClass(int subId, @ProfileClass int profileClass) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PROFILE_CLASS, profileClass,
SubscriptionInfoInternal.Builder::setProfileClass);
}
/**
* Set the subscription type.
*
* @param subId Subscription id.
* @param type Subscription type.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setSubscriptionType(int subId, @SubscriptionType int type) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SUBSCRIPTION_TYPE, type,
SubscriptionInfoInternal.Builder::setType);
}
/**
* Set the owner package of group the subscription belongs to.
*
* @param subId Subscription id.
* @param groupOwner Owner package of group the subscription belongs to.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setGroupOwner(int subId, @NonNull String groupOwner) {
Objects.requireNonNull(groupOwner);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_OWNER, groupOwner,
SubscriptionInfoInternal.Builder::setGroupOwner);
}
/**
* Set the enabled mobile data policies.
*
* @param subId Subscription id.
* @param enabledMobileDataPolicies The enabled mobile data policies.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setEnabledMobileDataPolicies(int subId, @NonNull String enabledMobileDataPolicies) {
Objects.requireNonNull(enabledMobileDataPolicies);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
enabledMobileDataPolicies,
SubscriptionInfoInternal.Builder::setEnabledMobileDataPolicies);
}
/**
* Set the IMSI (International Mobile Subscriber Identity) of the subscription.
*
* @param subId Subscription id.
* @param imsi The IMSI.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setImsi(int subId, @NonNull String imsi) {
Objects.requireNonNull(imsi);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMSI, imsi,
SubscriptionInfoInternal.Builder::setImsi);
}
/**
* Set whether Uicc applications are configured to enable or not.
*
* @param subId Subscription id.
* @param areUiccApplicationsEnabled if Uicc applications are configured to enable.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setUiccApplicationsEnabled(int subId, boolean areUiccApplicationsEnabled) {
setUiccApplicationsEnabled(subId, areUiccApplicationsEnabled ? 1 : 0);
}
/**
* Set whether Uicc applications are configured to enable or not.
*
* @param subId Subscription id.
* @param areUiccApplicationsEnabled if Uicc applications are configured to enable.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setUiccApplicationsEnabled(int subId, int areUiccApplicationsEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
areUiccApplicationsEnabled,
SubscriptionInfoInternal.Builder::setUiccApplicationsEnabled);
}
/**
* Set whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
* subscription.
*
* @param subId Subscription id.
* @param isRcsUceEnabled If the user enabled RCS UCE for this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setRcsUceEnabled(int subId, int isRcsUceEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, isRcsUceEnabled,
SubscriptionInfoInternal.Builder::setRcsUceEnabled);
}
/**
* Set whether the user has enabled cross SIM calling for this subscription.
*
* @param subId Subscription id.
* @param isCrossSimCallingEnabled If the user enabled cross SIM calling for this
* subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setCrossSimCallingEnabled(int subId, int isCrossSimCallingEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
isCrossSimCallingEnabled,
SubscriptionInfoInternal.Builder::setCrossSimCallingEnabled);
}
/**
* Set the RCS config for this subscription.
*
* @param subId Subscription id.
* @param rcsConfig The RCS config for this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setRcsConfig(int subId, @NonNull byte[] rcsConfig) {
Objects.requireNonNull(rcsConfig);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_RCS_CONFIG, rcsConfig,
SubscriptionInfoInternal.Builder::setRcsConfig);
}
/**
* Set the allowed network types for reasons.
*
* @param subId Subscription id.
* @param allowedNetworkTypesForReasons The allowed network types for reasons in string
* format. The format is "[reason]=[network types bitmask], [reason]=[network types bitmask],
* ...". For example, "user=1239287394, thermal=298791239, carrier=3456812312".
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setAllowedNetworkTypesForReasons(int subId,
@NonNull String allowedNetworkTypesForReasons) {
Objects.requireNonNull(allowedNetworkTypesForReasons);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
allowedNetworkTypesForReasons,
SubscriptionInfoInternal.Builder::setAllowedNetworkTypesForReasons);
}
/**
* Set device to device sharing status.
*
* @param subId Subscription id.
* @param deviceToDeviceStatusSharingPreference Device to device sharing status.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setDeviceToDeviceStatusSharingPreference(int subId,
@DeviceToDeviceStatusSharingPreference int deviceToDeviceStatusSharingPreference) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING,
deviceToDeviceStatusSharingPreference,
SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingPreference);
}
/**
* Set whether the user has opted-in voice over IMS.
*
* @param subId Subscription id.
* @param isVoImsOptInEnabled Whether the user has opted-in voice over IMS.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setVoImsOptInEnabled(int subId, int isVoImsOptInEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, isVoImsOptInEnabled,
SubscriptionInfoInternal.Builder::setVoImsOptInEnabled);
}
/**
* Set contacts information that allow device to device sharing.
*
* @param subId Subscription id.
* @param deviceToDeviceStatusSharingContacts contacts information that allow device to
* device sharing.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setDeviceToDeviceStatusSharingContacts(int subId,
@NonNull String deviceToDeviceStatusSharingContacts) {
Objects.requireNonNull(deviceToDeviceStatusSharingContacts);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
deviceToDeviceStatusSharingContacts,
SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingContacts);
}
/**
* Set whether the user has enabled NR advanced calling.
*
* @param subId Subscription id.
* @param isNrAdvancedCallingEnabled Whether the user has enabled NR advanced calling.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setNrAdvancedCallingEnabled(int subId, int isNrAdvancedCallingEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
isNrAdvancedCallingEnabled,
SubscriptionInfoInternal.Builder::setNrAdvancedCallingEnabled);
}
/**
* Set the phone number retrieved from carrier.
*
* @param subId Subscription id.
* @param numberFromCarrier The phone number retrieved from carrier.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setNumberFromCarrier(int subId, @NonNull String numberFromCarrier) {
Objects.requireNonNull(numberFromCarrier);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
numberFromCarrier, SubscriptionInfoInternal.Builder::setNumberFromCarrier);
}
/**
* Set the phone number retrieved from IMS.
*
* @param subId Subscription id.
* @param numberFromIms The phone number retrieved from IMS.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setNumberFromIms(int subId, @NonNull String numberFromIms) {
Objects.requireNonNull(numberFromIms);
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
numberFromIms, SubscriptionInfoInternal.Builder::setNumberFromIms);
}
/**
* Set the port index of the Uicc card.
*
* @param subId Subscription id.
* @param portIndex The port index of the Uicc card.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setPortIndex(int subId, int portIndex) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PORT_INDEX, portIndex,
SubscriptionInfoInternal.Builder::setPortIndex);
}
/**
* Set subscription's preferred usage setting.
*
* @param subId Subscription id.
* @param usageSetting Subscription's preferred usage setting.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setUsageSetting(int subId, @UsageSetting int usageSetting) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USAGE_SETTING, usageSetting,
SubscriptionInfoInternal.Builder::setUsageSetting);
}
/**
* Set last used TP message reference.
*
* @param subId Subscription id.
* @param lastUsedTPMessageReference Last used TP message reference.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setLastUsedTPMessageReference(int subId, int lastUsedTPMessageReference) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_TP_MESSAGE_REF,
lastUsedTPMessageReference,
SubscriptionInfoInternal.Builder::setLastUsedTPMessageReference);
}
/**
* Set the user id associated with this subscription.
*
* @param subId Subscription id.
* @param userId The user id associated with this subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setUserId(int subId, @UserIdInt int userId) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USER_HANDLE, userId,
SubscriptionInfoInternal.Builder::setUserId);
}
/**
* Set whether satellite is enabled or not.
*
* @param subId Subscription id.
* @param isSatelliteEnabled if satellite is enabled or not.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setSatelliteEnabled(int subId, int isSatelliteEnabled) {
writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SATELLITE_ENABLED,
isSatelliteEnabled,
SubscriptionInfoInternal.Builder::setSatelliteEnabled);
}
/**
* Set whether group of the subscription is disabled. This is only useful if it's a grouped
* opportunistic subscription. In this case, if all primary (non-opportunistic)
* subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
* we should disable this opportunistic subscription.
*
* @param subId Subscription id.
* @param isGroupDisabled if group of the subscription is disabled.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void setGroupDisabled(int subId, boolean isGroupDisabled) {
// group disabled does not have a corresponding SimInfo column. So we only update the cache.
// Grab the write lock so no other threads can read or write the cache.
mReadWriteLock.writeLock().lock();
try {
SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
if (subInfoCache == null) {
throw new IllegalArgumentException("setGroupDisabled: Subscription doesn't exist. "
+ "subId=" + subId);
}
mAllSubscriptionInfoInternalCache.put(subId,
new SubscriptionInfoInternal.Builder(subInfoCache)
.setGroupDisabled(isGroupDisabled).build());
} finally {
mReadWriteLock.writeLock().unlock();
}
}
/**
* Reload the database from content provider to the cache. This must be a synchronous operation
* to prevent cache/database out-of-sync. Callers should be cautious to call this method because
* it might take longer time to complete.
*/
public void reloadDatabaseSync() {
logl("reloadDatabaseSync");
// Synchronously load the database into the cache.
loadDatabaseInternal();
}
/**
* Load the database from content provider to the cache.
*/
private void loadDatabaseInternal() {
logl("loadDatabaseInternal");
try (Cursor cursor = mContext.getContentResolver().query(
SimInfo.CONTENT_URI, null, null, null, null)) {
mReadWriteLock.writeLock().lock();
try {
Map<Integer, SubscriptionInfoInternal> newAllSubscriptionInfoInternalCache =
new HashMap<>();
boolean changed = false;
while (cursor != null && cursor.moveToNext()) {
SubscriptionInfoInternal subInfo = createSubscriptionInfoFromCursor(cursor);
newAllSubscriptionInfoInternalCache.put(subInfo.getSubscriptionId(), subInfo);
if (!Objects.equals(mAllSubscriptionInfoInternalCache
.get(subInfo.getSubscriptionId()), subInfo)) {
mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(
subInfo.getSubscriptionId()));
changed = true;
}
}
if (changed) {
mAllSubscriptionInfoInternalCache.clear();
mAllSubscriptionInfoInternalCache.putAll(newAllSubscriptionInfoInternalCache);
logl("Loaded " + mAllSubscriptionInfoInternalCache.size()
+ " records from the subscription database.");
mAllSubscriptionInfoInternalCache.forEach(
(subId, subInfo) -> log(" " + subInfo.toString()));
}
} finally {
mReadWriteLock.writeLock().unlock();
}
}
}
/**
* Initialize the database cache. Load the entire database into the cache.
*/
private void initializeDatabase() {
if (mAsyncMode) {
// Load the database asynchronously.
post(() -> {
synchronized (this) {
loadDatabaseInternal();
mDatabaseInitialized = true;
mCallback.invokeFromExecutor(mCallback::onInitialized);
}
});
} else {
// Load the database synchronously.
synchronized (this) {
loadDatabaseInternal();
mDatabaseInitialized = true;
mCallback.invokeFromExecutor(mCallback::onInitialized);
}
}
}
/**
* Build the {@link SubscriptionInfoInternal} from database.
*
* @param cursor The cursor of the database.
*
* @return The subscription info from a single database record.
*/
@NonNull
private SubscriptionInfoInternal createSubscriptionInfoFromCursor(@NonNull Cursor cursor) {
SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder();
int id = cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
builder.setId(id)
.setIccId(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ICC_ID))))
.setSimSlotIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_SIM_SLOT_INDEX)))
.setDisplayName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_DISPLAY_NAME))))
.setCarrierName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CARRIER_NAME))))
.setDisplayNameSource(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_NAME_SOURCE)))
.setIconTint(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_COLOR)))
.setNumber(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_NUMBER))))
.setDataRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_DATA_ROAMING)))
.setMcc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_MCC_STRING))))
.setMnc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_MNC_STRING))))
.setEhplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_EHPLMNS))))
.setHplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_HPLMNS))))
.setEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IS_EMBEDDED)));
String cardString = TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CARD_ID)));
builder.setCardString(cardString);
// publicCardId is the publicly exposed int card ID
int publicCardId = mUiccController.convertToPublicCardId(cardString);
byte[] rules = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_ACCESS_RULES));
if (rules != null) {
builder.setNativeAccessRules(rules);
}
rules = cursor.getBlob(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS));
if (rules != null) {
builder.setCarrierConfigAccessRules(rules);
}
byte[] config = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG));
if (config != null) {
builder.setRcsConfig(config);
}
builder.setCardId(publicCardId)
.setRemovableEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IS_REMOVABLE)))
.setCellBroadcastExtremeThreatAlertEnabled(cursor.getInt(cursor
.getColumnIndexOrThrow(SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT)))
.setCellBroadcastSevereThreatAlertEnabled(cursor.getInt(cursor
.getColumnIndexOrThrow(SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT)))
.setCellBroadcastAmberAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_AMBER_ALERT)))
.setCellBroadcastEmergencyAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_EMERGENCY_ALERT)))
.setCellBroadcastAlertSoundDuration(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_ALERT_SOUND_DURATION)))
.setCellBroadcastAlertReminderInterval(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL)))
.setCellBroadcastAlertVibrationEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_ALERT_VIBRATE)))
.setCellBroadcastAlertSpeechEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_ALERT_SPEECH)))
.setCellBroadcastEtwsTestAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_ETWS_TEST_ALERT)))
.setCellBroadcastAreaInfoMessageEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_CHANNEL_50_ALERT)))
.setCellBroadcastTestAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_CMAS_TEST_ALERT)))
.setCellBroadcastOptOutDialogEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CB_OPT_OUT_DIALOG)))
.setEnhanced4GModeEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED)))
.setVideoTelephonyEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_VT_IMS_ENABLED)))
.setWifiCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_WFC_IMS_ENABLED)))
.setWifiCallingMode(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_WFC_IMS_MODE)))
.setWifiCallingModeForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_WFC_IMS_ROAMING_MODE)))
.setWifiCallingEnabledForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED)))
.setOpportunistic(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IS_OPPORTUNISTIC)))
.setGroupUuid(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_GROUP_UUID))))
.setCountryIso(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ISO_COUNTRY_CODE))))
.setCarrierId(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CARRIER_ID)))
.setProfileClass(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_PROFILE_CLASS)))
.setType(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_SUBSCRIPTION_TYPE)))
.setGroupOwner(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_GROUP_OWNER))))
.setEnabledMobileDataPolicies(TextUtils.emptyIfNull(
cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES))))
.setImsi(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IMSI))))
.setUiccApplicationsEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED)))
.setRcsUceEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)))
.setCrossSimCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)))
.setAllowedNetworkTypesForReasons(TextUtils.emptyIfNull(
cursor.getString(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS))))
.setDeviceToDeviceStatusSharingPreference(cursor.getInt(
cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_D2D_STATUS_SHARING)))
.setVoImsOptInEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)))
.setDeviceToDeviceStatusSharingContacts(TextUtils.emptyIfNull(cursor.getString(
cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS))))
.setNrAdvancedCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED)))
.setNumberFromCarrier(TextUtils.emptyIfNull(cursor.getString(
cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER))))
.setNumberFromIms(TextUtils.emptyIfNull(cursor.getString(
cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS))))
.setPortIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_PORT_INDEX)))
.setUsageSetting(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_USAGE_SETTING)))
.setLastUsedTPMessageReference(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_TP_MESSAGE_REF)))
.setUserId(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_USER_HANDLE)))
.setSatelliteEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_SATELLITE_ENABLED)));
return builder.build();
}
/**
* Sync the group sharing fields from reference subscription to the rest of the subscriptions in
* the same group. For example, if user enables wifi calling, the same setting should be applied
* to all subscriptions in the same group.
*
* @param subId The subscription id of reference subscription.
*
* @throws IllegalArgumentException if the subscription does not exist.
*/
public void syncToGroup(int subId) {
if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) {
throw new IllegalArgumentException("Invalid subId " + subId);
}
for (String column : GROUP_SHARING_COLUMNS) {
// Get the value from the reference subscription, and set to itself again.
// writeDatabaseAndCacheHelper() will automatically sync to the rest of the group.
setSubscriptionProperty(subId, column, getSubscriptionProperty(subId, column));
}
}
/**
* 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) {
mReadWriteLock.readLock().lock();
try {
return mAllSubscriptionInfoInternalCache.get(subId);
} finally {
mReadWriteLock.readLock().unlock();
}
}
/**
* @return All subscription infos in the database.
*/
@NonNull
public List<SubscriptionInfoInternal> getAllSubscriptions() {
mReadWriteLock.readLock().lock();
try {
return new ArrayList<>(mAllSubscriptionInfoInternalCache.values());
} finally {
mReadWriteLock.readLock().unlock();
}
}
/**
* 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();
}
}
/**
* Log debug messages.
*
* @param s debug messages
*/
private void log(@NonNull String s) {
Rlog.d(LOG_TAG, s);
}
/**
* Log error messages.
*
* @param s error messages
*/
private void loge(@NonNull String s) {
Rlog.e(LOG_TAG, s);
}
/**
* Log verbose messages.
*
* @param s debug messages.
*/
private void logv(@NonNull String s) {
if (VDBG) Rlog.v(LOG_TAG, s);
}
/**
* Log error messages and also log into the local log.
*
* @param s debug messages
*/
private void logel(@NonNull String s) {
loge(s);
mLocalLog.log(s);
}
/**
* Log debug messages and also log into the local log.
*
* @param s debug messages
*/
private void logl(@NonNull String s) {
log(s);
mLocalLog.log(s);
}
/**
* Dump the state of {@link SubscriptionDatabaseManager}.
*
* @param fd File descriptor
* @param printWriter Print writer
* @param args Arguments
*/
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter printWriter,
@NonNull String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println(SubscriptionDatabaseManager.class.getSimpleName() + ":");
pw.increaseIndent();
pw.println("All subscriptions:");
pw.increaseIndent();
mReadWriteLock.readLock().lock();
try {
mAllSubscriptionInfoInternalCache.forEach((subId, subInfo) -> pw.println(subInfo));
} finally {
mReadWriteLock.readLock().unlock();
}
pw.decreaseIndent();
pw.println();
pw.println("mAsyncMode=" + mAsyncMode);
synchronized (this) {
pw.println("mDatabaseInitialized=" + mDatabaseInitialized);
}
pw.println("mReadWriteLock=" + mReadWriteLock);
pw.println();
pw.println("Local log:");
pw.increaseIndent();
mLocalLog.dump(fd, printWriter, args);
pw.decreaseIndent();
pw.decreaseIndent();
}
}