Merge "[Settings] Add the repository for room database"
diff --git a/src/com/android/settings/network/MobileNetworkRepository.java b/src/com/android/settings/network/MobileNetworkRepository.java
new file mode 100644
index 0000000..912b36b
--- /dev/null
+++ b/src/com/android/settings/network/MobileNetworkRepository.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 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.settings.network;
+
+import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
+import android.telephony.UiccPortInfo;
+import android.telephony.UiccSlotInfo;
+import android.util.Log;
+
+import com.android.settings.network.telephony.MobileNetworkUtils;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.mobile.dataservice.MobileNetworkDatabase;
+import com.android.settingslib.mobile.dataservice.MobileNetworkInfoDao;
+import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoDao;
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
+import com.android.settingslib.mobile.dataservice.UiccInfoDao;
+import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+
+public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptionsChangedListener {
+
+ private static final String TAG = "MobileNetworkRepository";
+
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
+ private MobileNetworkDatabase mMobileNetworkDatabase;
+ private SubscriptionInfoDao mSubscriptionInfoDao;
+ private UiccInfoDao mUiccInfoDao;
+ private MobileNetworkInfoDao mMobileNetworkInfoDao;
+ private List<SubscriptionInfoEntity> mAvailableSubInfoEntityList = new ArrayList<>();
+ private List<SubscriptionInfoEntity> mActiveSubInfoEntityList = new ArrayList<>();
+ private List<UiccInfoEntity> mUiccInfoEntityList = new ArrayList<>();
+ private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
+ private MobileNetworkCallback mCallback;
+ private Context mContext;
+ private AirplaneModeObserver mAirplaneModeObserver;
+ private Uri mAirplaneModeSettingUri;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+
+ private int mPhysicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private int mLogicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private int mCardState = UiccSlotInfo.CARD_STATE_INFO_ABSENT;
+ private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
+ private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private boolean mIsEuicc = false;
+ private boolean mIsRemovable = false;
+ private boolean mIsActive = false;
+
+ MobileNetworkRepository(Context context, MobileNetworkCallback mobileNetworkCallback) {
+ mContext = context;
+ mCallback = mobileNetworkCallback;
+ mMobileNetworkDatabase = MobileNetworkDatabase.createDatabase(context);
+ mSubscriptionInfoDao = mMobileNetworkDatabase.mSubscriptionInfoDao();
+ mUiccInfoDao = mMobileNetworkDatabase.mUiccInfoDao();
+ mMobileNetworkInfoDao = mMobileNetworkDatabase.mMobileNetworkInfoDao();
+ mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+ mAirplaneModeObserver = new AirplaneModeObserver(new Handler(Looper.getMainLooper()));
+ mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_MOBILE_NETWORK_DB_CREATED);
+ }
+
+ private class AirplaneModeObserver extends ContentObserver {
+ AirplaneModeObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(Context context) {
+ context.getContentResolver().registerContentObserver(mAirplaneModeSettingUri, false,
+ this);
+ }
+
+ public void unRegister(Context context) {
+ context.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(mAirplaneModeSettingUri)) {
+ mCallback.onAirplaneModeChanged(isAirplaneModeOn());
+ }
+ }
+ }
+
+ public void addRegister(LifecycleOwner lifecycleOwner) {
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mContext.getMainExecutor(), this);
+ mAirplaneModeObserver.register(mContext);
+ observeAllSubInfo(lifecycleOwner);
+ observeAllUiccInfo(lifecycleOwner);
+ observeAllMobileNetworkInfo(lifecycleOwner);
+ }
+
+ public void removeRegister() {
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
+ mAirplaneModeObserver.unRegister(mContext);
+ mContext.getContentResolver().unregisterContentObserver(mAirplaneModeObserver);
+ }
+
+ private void observeAllSubInfo(LifecycleOwner lifecycleOwner) {
+ Log.d(TAG, "Observe subInfo.");
+ mMobileNetworkDatabase.queryAvailableSubInfos().observe(
+ lifecycleOwner, this::onAvailableSubInfoChanged);
+ }
+
+ private void observeAllUiccInfo(LifecycleOwner lifecycleOwner) {
+ Log.d(TAG, "Observe UICC info.");
+ mMobileNetworkDatabase.queryAllUiccInfo().observe(
+ lifecycleOwner, this::onAllUiccInfoChanged);
+ }
+
+ private void observeAllMobileNetworkInfo(LifecycleOwner lifecycleOwner) {
+ Log.d(TAG, "Observe mobile network info.");
+ mMobileNetworkDatabase.queryAllMobileNetworkInfo().observe(
+ lifecycleOwner, this::onAllMobileNetworkInfoChanged);
+ }
+
+ public List<SubscriptionInfoEntity> getAvailableSubInfoEntityList() {
+ return mAvailableSubInfoEntityList;
+ }
+
+ public List<SubscriptionInfoEntity> getActiveSubscriptionInfoList() {
+ return mActiveSubInfoEntityList;
+ }
+
+ public List<UiccInfoEntity> getUiccInfoEntityList() {
+ return mUiccInfoEntityList;
+ }
+
+ public List<MobileNetworkInfoEntity> getMobileNetworkInfoEntityList() {
+ return mMobileNetworkInfoEntityList;
+ }
+
+ public int getSubInfosCount() {
+ return mSubscriptionInfoDao.count();
+ }
+
+ public int getUiccInfosCount() {
+ return mUiccInfoDao.count();
+ }
+
+ public int getMobileNetworkInfosCount() {
+ return mMobileNetworkInfoDao.count();
+ }
+
+ private void getUiccInfoBySubscriptionInfo(UiccSlotInfo[] uiccSlotInfos,
+ SubscriptionInfo subInfo) {
+ for (int i = 0; i < uiccSlotInfos.length; i++) {
+ UiccSlotInfo curSlotInfo = uiccSlotInfos[i];
+ if (curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT) {
+ mIsEuicc = curSlotInfo.getIsEuicc();
+ mCardState = curSlotInfo.getCardStateInfo();
+ mIsRemovable = curSlotInfo.isRemovable();
+ mCardId = subInfo.getCardId();
+
+ Collection<UiccPortInfo> uiccPortInfos = curSlotInfo.getPorts();
+ for (UiccPortInfo portInfo : uiccPortInfos) {
+ if (portInfo.getPortIndex() == subInfo.getPortIndex()
+ && portInfo.getLogicalSlotIndex() == subInfo.getSimSlotIndex()) {
+ mPhysicalSlotIndex = i;
+ mLogicalSlotIndex = portInfo.getLogicalSlotIndex();
+ mIsActive = portInfo.isActive();
+ mPortIndex = portInfo.getPortIndex();
+ break;
+ } else {
+ Log.d(TAG,
+ "Can not get port index and physicalSlotIndex for subId " + mSubId);
+ }
+ }
+ if (mPhysicalSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ break;
+ }
+ } else {
+ Log.d(TAG, "Can not get card state info");
+ }
+ }
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_GET_UICC_INFO);
+ }
+
+ private void onAvailableSubInfoChanged(
+ List<SubscriptionInfoEntity> availableSubInfoEntityList) {
+ mAvailableSubInfoEntityList = availableSubInfoEntityList;
+ mActiveSubInfoEntityList = mAvailableSubInfoEntityList.stream()
+ .filter(SubscriptionInfoEntity::isActiveSubscription)
+ .filter(SubscriptionInfoEntity::isSubscriptionVisible)
+ .collect(Collectors.toList());
+ Log.d(TAG, "onAvailableSubInfoChanged, availableSubInfoEntityList = "
+ + availableSubInfoEntityList);
+ mCallback.onAvailableSubInfoChanged(availableSubInfoEntityList);
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_SUB_INFO_IS_CHANGED);
+ setActiveSubInfoList(mActiveSubInfoEntityList);
+ }
+
+ private void setActiveSubInfoList(
+ List<SubscriptionInfoEntity> activeSubInfoEntityList) {
+ Log.d(TAG, "onActiveSubInfoChanged, activeSubInfoEntityList = " + activeSubInfoEntityList);
+ mCallback.onActiveSubInfoChanged(mActiveSubInfoEntityList);
+ }
+
+ private void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList) {
+ mUiccInfoEntityList = uiccInfoEntityList;
+ mCallback.onAllUiccInfoChanged(uiccInfoEntityList);
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_UICC_INFO_IS_CHANGED);
+ }
+
+ private void onAllMobileNetworkInfoChanged(
+ List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
+ mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList;
+ mCallback.onAllMobileNetworkInfoChanged(mobileNetworkInfoEntityList);
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_MOBILE_NETWORK_INFO_IS_CHANGED);
+ }
+
+ public void insertSubInfo(Context context, SubscriptionInfo info) {
+ mExecutor.execute(() -> {
+ SubscriptionInfoEntity subInfoEntity =
+ convertToSubscriptionInfoEntity(context, info);
+ if (subInfoEntity != null) {
+ mMobileNetworkDatabase.insertSubsInfo(subInfoEntity);
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_SUB_INFO);
+ } else {
+ Log.d(TAG, "Can not insert subInfo, the entity is null");
+ }
+ });
+ }
+
+ public void deleteAllInfoBySubId(String subId) {
+ mExecutor.execute(() -> {
+ mMobileNetworkDatabase.deleteSubInfoBySubId(subId);
+ mMobileNetworkDatabase.deleteUiccInfoBySubId(subId);
+ mMobileNetworkDatabase.deleteMobileNetworkInfoBySubId(subId);
+ });
+ mAvailableSubInfoEntityList.removeIf(info -> info.subId.equals(subId));
+ mActiveSubInfoEntityList.removeIf(info -> info.subId.equals(subId));
+ mUiccInfoEntityList.removeIf(info -> info.subId.equals(subId));
+ mMobileNetworkInfoEntityList.removeIf(info -> info.subId.equals(subId));
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_DELETE_DATA);
+ }
+
+ public SubscriptionInfoEntity convertToSubscriptionInfoEntity(Context context,
+ SubscriptionInfo subInfo) {
+ mSubId = subInfo.getSubscriptionId();
+ mTelephonyManager = context.getSystemService(
+ TelephonyManager.class).createForSubscriptionId(mSubId);
+
+ UiccSlotInfo[] uiccSlotInfos = mTelephonyManager.getUiccSlotsInfo();
+ if (uiccSlotInfos == null || uiccSlotInfos.length == 0) {
+ Log.d(TAG, "uiccSlotInfos = null or empty");
+ return null;
+ } else {
+ getUiccInfoBySubscriptionInfo(uiccSlotInfos, subInfo);
+ insertUiccInfo();
+ insertMobileNetworkInfo(context);
+ Log.d(TAG, "convert subscriptionInfo to entity for subId = " + mSubId);
+ return new SubscriptionInfoEntity(String.valueOf(mSubId),
+ subInfo.getSimSlotIndex(),
+ subInfo.getCarrierId(), subInfo.getDisplayName().toString(),
+ subInfo.getCarrierName() != null ? subInfo.getCarrierName().toString() : "",
+ subInfo.getDataRoaming(), subInfo.getMccString(), subInfo.getMncString(),
+ subInfo.getCountryIso(), subInfo.isEmbedded(), mCardId,
+ subInfo.getPortIndex(), subInfo.isOpportunistic(),
+ String.valueOf(subInfo.getGroupUuid()),
+ subInfo.getSubscriptionType(),
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, context).toString(),
+ SubscriptionUtil.isSubscriptionVisible(mSubscriptionManager, context, subInfo),
+ SubscriptionUtil.getFormattedPhoneNumber(context, subInfo),
+ SubscriptionUtil.getFirstRemovableSubscription(context) == null ? false
+ : SubscriptionUtil.getFirstRemovableSubscription(
+ context).getSubscriptionId() == mSubId,
+ String.valueOf(SubscriptionUtil.getDefaultSimConfig(context, mSubId)),
+ SubscriptionUtil.getSubscriptionOrDefault(context, mSubId).getSubscriptionId()
+ == mSubId,
+ mSubscriptionManager.isValidSubscriptionId(mSubId),
+ mSubscriptionManager.isUsableSubscriptionId(mSubId),
+ mSubscriptionManager.isActiveSubscriptionId(mSubId),
+ true /*availableSubInfo*/,
+ mSubscriptionManager.getDefaultVoiceSubscriptionId() == mSubId,
+ mSubscriptionManager.getDefaultSmsSubscriptionId() == mSubId,
+ mSubscriptionManager.getDefaultDataSubscriptionId() == mSubId,
+ mSubscriptionManager.getDefaultSubscriptionId() == mSubId);
+ }
+ }
+
+ public void insertUiccInfo() {
+ mMobileNetworkDatabase.insertUiccInfo(convertToUiccInfoEntity());
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_UICC_INFO);
+ }
+
+ public void insertMobileNetworkInfo(Context context) {
+ mMobileNetworkDatabase.insertMobileNetworkInfo(convertToMobileNetworkInfoEntity(context));
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_MOBILE_NETWORK_INFO);
+ }
+
+ public MobileNetworkInfoEntity convertToMobileNetworkInfoEntity(Context context) {
+ return new MobileNetworkInfoEntity(String.valueOf(mSubId),
+ MobileNetworkUtils.isContactDiscoveryEnabled(context, mSubId),
+ MobileNetworkUtils.isContactDiscoveryVisible(context, mSubId),
+ MobileNetworkUtils.isMobileDataEnabled(context),
+ MobileNetworkUtils.isCdmaOptions(context, mSubId),
+ MobileNetworkUtils.isGsmOptions(context, mSubId),
+ MobileNetworkUtils.isWorldMode(context, mSubId),
+ MobileNetworkUtils.shouldDisplayNetworkSelectOptions(context, mSubId),
+ MobileNetworkUtils.isTdscdmaSupported(context, mSubId),
+ MobileNetworkUtils.activeNetworkIsCellular(context),
+ SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)
+ );
+ }
+
+ private UiccInfoEntity convertToUiccInfoEntity() {
+ return new UiccInfoEntity(String.valueOf(mSubId), String.valueOf(mPhysicalSlotIndex),
+ mLogicalSlotIndex, mCardId, mIsEuicc, isMultipleEnabledProfilesSupported(),
+ mCardState, mIsRemovable, mIsActive, mPortIndex
+ );
+ }
+
+ private boolean isMultipleEnabledProfilesSupported() {
+ List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
+ if (cardInfos == null) {
+ Log.w(TAG, "UICC card info list is empty.");
+ return false;
+ }
+ return cardInfos.stream().anyMatch(
+ cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ insertAvailableSubInfoToEntity(mSubscriptionManager.getAvailableSubscriptionInfoList());
+ }
+
+ private void insertAvailableSubInfoToEntity(List<SubscriptionInfo> availableInfoList) {
+ if ((availableInfoList == null || availableInfoList.size() == 0)
+ && mAvailableSubInfoEntityList.size() != 0) {
+ Log.d(TAG, "availableSudInfoList from framework is empty, remove all subs");
+ for (SubscriptionInfoEntity info : mAvailableSubInfoEntityList) {
+ deleteAllInfoBySubId(info.subId);
+ }
+ } else if (availableInfoList != null) {
+ for (SubscriptionInfo subInfo : availableInfoList) {
+ if (availableInfoList.size() < mAvailableSubInfoEntityList.size()) {
+ Optional<SubscriptionInfoEntity> infoEntity =
+ mAvailableSubInfoEntityList.stream().filter(
+ info -> subInfo.getSubscriptionId()
+ != Integer.parseInt(info.subId)).findFirst();
+
+ if (infoEntity.isPresent()) {
+ Log.d(TAG, "delete sudInfo " + infoEntity.get().subId
+ + " from subInfoEntity");
+ deleteAllInfoBySubId(infoEntity.get().subId);
+ }
+ }
+
+ Log.d(TAG, "insert sudInfo " + subInfo.getSubscriptionId() + " to subInfoEntity");
+ insertSubInfo(mContext, subInfo);
+ }
+ }
+ }
+
+ public boolean isAirplaneModeOn() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
+ /**
+ * Callback for clients to get the latest info changes if the framework or content observers.
+ * updates the relevant info.
+ */
+ interface MobileNetworkCallback {
+ void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList);
+
+ void onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList);
+
+ void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList);
+
+ void onAllMobileNetworkInfoChanged(
+ List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList);
+
+ void onAirplaneModeChanged(boolean enabled);
+ }
+}