Merge changes Iccd209fd,Ia0c32882 into main

* changes:
  Refactor signal strength in SIM status
  Refactor SimStatusDialogRepository
This commit is contained in:
Chaohui Wang
2024-05-29 04:13:32 +00:00
committed by Android (Google) Code Review
9 changed files with 512 additions and 419 deletions

View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.simstatus
import android.content.Context
import android.telephony.SubscriptionManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.network.telephony.SimSlotRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
class ImsRegistrationStateController @JvmOverloads constructor(
private val context: Context,
private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
private val imsMmTelRepositoryFactory: (subId: Int) -> ImsMmTelRepository = { subId ->
ImsMmTelRepositoryImpl(context, subId)
},
) {
fun collectImsRegistered(
lifecycleOwner: LifecycleOwner,
simSlotIndex: Int,
action: (imsRegistered: Boolean) -> Unit,
) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
imsRegisteredFlow(simSlotIndex).collect(action)
}
}
}
private fun imsRegisteredFlow(simSlotIndex: Int): Flow<Boolean> =
simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
.flatMapLatest { subId ->
if (SubscriptionManager.isValidSubscriptionId(subId)) {
imsMmTelRepositoryFactory(subId).imsRegisteredFlow()
} else {
flowOf(false)
}
}
.conflate()
.flowOn(Dispatchers.Default)
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.simstatus
import android.content.Context
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
import android.util.Log
import com.android.settings.R
import com.android.settings.network.telephony.serviceStateFlow
import com.android.settings.network.telephony.telephonyCallbackFlow
import com.android.settingslib.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@OptIn(ExperimentalCoroutinesApi::class)
class SignalStrengthRepository(
private val context: Context,
private val serviceStateFlowFactory: (subId: Int) -> Flow<ServiceState> = {
context.serviceStateFlow(it)
},
) {
fun signalStrengthDisplayFlow(subId: Int): Flow<String> =
serviceStateFlowFactory(subId).flatMapLatest { serviceState ->
if (Utils.isInService(serviceState)) {
signalStrengthFlow(subId).map { it.displayString() }
} else {
flowOf("0")
}
}.conflate().flowOn(Dispatchers.Default)
/** Creates an instance of a cold Flow for [SignalStrength] of given [subId]. */
private fun signalStrengthFlow(subId: Int): Flow<SignalStrength> =
context.telephonyCallbackFlow(subId) {
object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener {
override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
trySend(signalStrength)
val cellSignalStrengths = signalStrength.cellSignalStrengths
Log.d(TAG, "[$subId] onSignalStrengthsChanged: $cellSignalStrengths")
}
}
}
private fun SignalStrength.displayString() =
context.getString(R.string.sim_signal_strength, signalDbm(), signalAsu())
private companion object {
private const val TAG = "SignalStrengthRepo"
private fun SignalStrength.signalDbm(): Int =
cellSignalStrengths.firstOrNull { it.dbm != -1 }?.dbm ?: 0
private fun SignalStrength.signalAsu(): Int =
cellSignalStrengths.firstOrNull { it.asuLevel != -1 }?.asuLevel ?: 0
}
}

View File

@@ -32,10 +32,8 @@ import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
import android.telephony.CellBroadcastIntents;
import android.telephony.CellBroadcastService;
import android.telephony.CellSignalStrength;
import android.telephony.ICellBroadcastService;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -113,7 +111,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
private SubscriptionInfo mSubscriptionInfo;
private TelephonyDisplayInfo mTelephonyDisplayInfo;
private ServiceState mPreviousServiceState;
private final int mSlotIndex;
private TelephonyManager mTelephonyManager;
@@ -219,15 +216,12 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
// getServiceState() may return null when the subscription is inactive
// or when there was an error communicating with the phone process.
final ServiceState serviceState = getTelephonyManager().getServiceState();
final SignalStrength signalStrength = getTelephonyManager().getSignalStrength();
updatePhoneNumber();
updateServiceState(serviceState);
updateSignalStrength(signalStrength);
updateNetworkType();
updateRoamingStatus(serviceState);
updateIccidNumber();
updateImsRegistrationState();
}
/**
@@ -257,7 +251,7 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
.registerTelephonyCallback(mContext.getMainExecutor(), mTelephonyCallback);
mSubscriptionManager.addOnSubscriptionsChangedListener(
mContext.getMainExecutor(), mOnSubscriptionsChangedListener);
collectImsRegistered(owner);
collectSimStatusDialogInfo(owner);
if (mShowLatestAreaInfo) {
updateAreaInfoText();
@@ -420,12 +414,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
private void updateServiceState(ServiceState serviceState) {
final int state = Utils.getCombinedServiceState(serviceState);
if (!Utils.isInService(serviceState)) {
resetSignalStrength();
} else if (!Utils.isInService(mPreviousServiceState)) {
// If ServiceState changed from out of service -> in service, update signal strength.
updateSignalStrength(getTelephonyManager().getSignalStrength());
}
String serviceStateValue;
@@ -450,49 +438,11 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
mDialog.setText(SERVICE_STATE_VALUE_ID, serviceStateValue);
}
private void updateSignalStrength(SignalStrength signalStrength) {
if (signalStrength == null) {
return;
}
// by default we show the signal strength
boolean showSignalStrength = true;
if (mSubscriptionInfo != null) {
final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
final PersistableBundle carrierConfig =
mCarrierConfigManager.getConfigForSubId(subscriptionId);
if (carrierConfig != null) {
showSignalStrength = carrierConfig.getBoolean(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL);
}
}
if (!showSignalStrength) {
mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID);
mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID);
return;
}
ServiceState serviceState = getTelephonyManager().getServiceState();
if (!Utils.isInService(serviceState)) {
return;
}
int signalDbm = getDbm(signalStrength);
int signalAsu = getAsuLevel(signalStrength);
if (signalDbm == -1) {
signalDbm = 0;
}
if (signalAsu == -1) {
signalAsu = 0;
}
mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, mRes.getString(R.string.sim_signal_strength,
signalDbm, signalAsu));
}
private void resetSignalStrength() {
mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, "0");
private void updateSignalStrength(@Nullable String signalStrength) {
boolean isVisible = signalStrength != null;
mDialog.setSettingVisibility(SIGNAL_STRENGTH_LABEL_ID, isVisible);
mDialog.setSettingVisibility(SIGNAL_STRENGTH_VALUE_ID, isVisible);
mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, signalStrength);
}
private void updateNetworkType() {
@@ -581,39 +531,21 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
}
}
private boolean isImsRegistrationStateShowUp() {
if (mSubscriptionInfo == null) {
return false;
}
final int subscriptionId = mSubscriptionInfo.getSubscriptionId();
final PersistableBundle carrierConfig =
mCarrierConfigManager.getConfigForSubId(subscriptionId);
return carrierConfig == null ? false :
carrierConfig.getBoolean(
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
private void updateImsRegistrationState(@Nullable Boolean imsRegistered) {
boolean isVisible = imsRegistered != null;
mDialog.setSettingVisibility(IMS_REGISTRATION_STATE_LABEL_ID, isVisible);
mDialog.setSettingVisibility(IMS_REGISTRATION_STATE_VALUE_ID, isVisible);
int stringId = Boolean.TRUE.equals(imsRegistered)
? com.android.settingslib.R.string.ims_reg_status_registered
: com.android.settingslib.R.string.ims_reg_status_not_registered;
mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(stringId));
}
private void updateImsRegistrationState() {
if (isImsRegistrationStateShowUp()) {
return;
}
mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_LABEL_ID);
mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_VALUE_ID);
}
private void collectImsRegistered(@NonNull LifecycleOwner owner) {
if (!isImsRegistrationStateShowUp()) {
return;
}
new ImsRegistrationStateController(mContext).collectImsRegistered(
owner, mSlotIndex, (Boolean imsRegistered) -> {
if (imsRegistered) {
mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(
com.android.settingslib.R.string.ims_reg_status_registered));
} else {
mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(
com.android.settingslib.R.string.ims_reg_status_not_registered));
}
private void collectSimStatusDialogInfo(@NonNull LifecycleOwner owner) {
new SimStatusDialogRepository(mContext).collectSimStatusDialogInfo(
owner, mSlotIndex, (simStatusDialogInfo) -> {
updateSignalStrength(simStatusDialogInfo.getSignalStrength());
updateImsRegistrationState(simStatusDialogInfo.getImsRegistered());
return Unit.INSTANCE;
}
);
@@ -623,44 +555,9 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
return SubscriptionManager.from(mContext).getActiveSubscriptionInfoForSimSlotIndex(slotId);
}
private int getDbm(SignalStrength signalStrength) {
List<CellSignalStrength> cellSignalStrengthList = signalStrength.getCellSignalStrengths();
int dbm = -1;
if (cellSignalStrengthList == null) {
return dbm;
}
for (CellSignalStrength cell : cellSignalStrengthList) {
if (cell.getDbm() != -1) {
dbm = cell.getDbm();
break;
}
}
return dbm;
}
private int getAsuLevel(SignalStrength signalStrength) {
List<CellSignalStrength> cellSignalStrengthList = signalStrength.getCellSignalStrengths();
int asu = -1;
if (cellSignalStrengthList == null) {
return asu;
}
for (CellSignalStrength cell : cellSignalStrengthList) {
if (cell.getAsuLevel() != -1) {
asu = cell.getAsuLevel();
break;
}
}
return asu;
}
@VisibleForTesting
class SimStatusDialogTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.DisplayInfoListener {
@Override
@@ -669,17 +566,11 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
updateNetworkType();
}
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
updateSignalStrength(signalStrength);
}
@Override
public void onServiceStateChanged(ServiceState serviceState) {
updateNetworkProvider();
updateServiceState(serviceState);
updateRoamingStatus(serviceState);
mPreviousServiceState = serviceState;
}
@Override

View File

@@ -26,6 +26,7 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -91,6 +92,13 @@ public class SimStatusDialogFragment extends InstrumentedDialogFragment {
super.onDestroy();
}
public void setSettingVisibility(int viewId, boolean isVisible) {
final View view = mRootView.findViewById(viewId);
if (view != null) {
view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
}
public void removeSettingFromScreen(int viewId) {
final View view = mRootView.findViewById(viewId);
if (view != null) {
@@ -106,7 +114,7 @@ public class SimStatusDialogFragment extends InstrumentedDialogFragment {
SimStatusDialogController.PHONE_NUMBER_VALUE_ID)
.sorted().toArray();
public void setText(int viewId, CharSequence text) {
public void setText(int viewId, @Nullable CharSequence text) {
if (!isAdded()) {
Log.d(TAG, "Fragment not attached yet.");
return;

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.simstatus
import android.content.Context
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.network.telephony.SimSlotRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.safeGetConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
class SimStatusDialogRepository @JvmOverloads constructor(
private val context: Context,
private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
private val signalStrengthRepository: SignalStrengthRepository =
SignalStrengthRepository(context),
private val imsMmTelRepositoryFactory: (subId: Int) -> ImsMmTelRepository = { subId ->
ImsMmTelRepositoryImpl(context, subId)
},
) {
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
data class SimStatusDialogInfo(
val signalStrength: String? = null,
val imsRegistered: Boolean? = null,
)
private data class SimStatusDialogVisibility(
val signalStrengthShowUp: Boolean,
val imsRegisteredShowUp: Boolean,
)
fun collectSimStatusDialogInfo(
lifecycleOwner: LifecycleOwner,
simSlotIndex: Int,
action: (info: SimStatusDialogInfo) -> Unit,
) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
simStatusDialogInfoBySlotFlow(simSlotIndex).collect(action)
}
}
}
private fun simStatusDialogInfoBySlotFlow(simSlotIndex: Int): Flow<SimStatusDialogInfo> =
simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
.flatMapLatest { subId ->
if (SubscriptionManager.isValidSubscriptionId(subId)) {
simStatusDialogInfoFlow(subId)
} else {
flowOf(SimStatusDialogInfo())
}
}
.conflate()
.flowOn(Dispatchers.Default)
private fun simStatusDialogInfoFlow(subId: Int): Flow<SimStatusDialogInfo> =
showUpFlow(subId).flatMapLatest { visibility ->
combine(
if (visibility.signalStrengthShowUp) {
signalStrengthRepository.signalStrengthDisplayFlow(subId)
} else flowOf(null),
if (visibility.imsRegisteredShowUp) {
imsMmTelRepositoryFactory(subId).imsRegisteredFlow()
} else flowOf(null),
) { signalStrength, imsRegistered ->
SimStatusDialogInfo(signalStrength = signalStrength, imsRegistered = imsRegistered)
}
}
private fun showUpFlow(subId: Int) = flow {
val config = carrierConfigManager.safeGetConfig(
keys = listOf(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
),
subId = subId,
)
val visibility = SimStatusDialogVisibility(
signalStrengthShowUp = config.getBoolean(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
true, // by default we show the signal strength in sim status
),
imsRegisteredShowUp = config.getBoolean(
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL
),
)
emit(visibility)
}
}