Snap for 11521484 from bec4c9573e to 24Q2-release

Change-Id: I086a5d8514a9b53b90a94e4e4eb47dac4e074bcf
This commit is contained in:
Android Build Coastguard Worker
2024-03-02 22:21:12 +00:00
7 changed files with 161 additions and 327 deletions

View File

@@ -686,7 +686,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothLeAudioHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
controllers.add(new NfcStackDebugLogPreferenceController(context));
controllers.add(new NfcSnoopLogPreferenceController(context, fragment));
controllers.add(new NfcVerboseVendorLogPreferenceController(context, fragment));
controllers.add(new ShowTapsPreferenceController(context));

View File

@@ -1,82 +0,0 @@
/*
* Copyright (C) 2021 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.development;
import android.content.Context;
import android.os.SystemProperties;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class NfcStackDebugLogPreferenceController extends
DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
PreferenceControllerMixin {
private static final String NFC_STACK_DEBUGLOG_ENABLED_KEY =
"nfc_stack_debuglog_enabled";
@VisibleForTesting
static final String NFC_STACK_DEBUGLOG_ENABLED_PROPERTY =
"persist.nfc.debug_enabled";
public NfcStackDebugLogPreferenceController(Context context) {
super(context);
}
@Override
public String getPreferenceKey() {
return NFC_STACK_DEBUGLOG_ENABLED_KEY;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
try {
SystemProperties.set(NFC_STACK_DEBUGLOG_ENABLED_PROPERTY,
isEnabled ? "true" : "false");
} catch (RuntimeException e) {
Log.e(TAG, "Fail to set nfc system property: " + e.getMessage());
}
return true;
}
@Override
public void updateState(Preference preference) {
try {
final boolean isEnabled = SystemProperties.getBoolean(
NFC_STACK_DEBUGLOG_ENABLED_PROPERTY, false /* default */);
((TwoStatePreference) mPreference).setChecked(isEnabled);
} catch (RuntimeException e) {
Log.e(TAG, "Fail to get nfc system property: " + e.getMessage());
}
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
try {
SystemProperties.set(NFC_STACK_DEBUGLOG_ENABLED_PROPERTY, "false");
((TwoStatePreference) mPreference).setChecked(false);
} catch (RuntimeException e) {
Log.e(TAG, "Fail to set nfc system property: " + e.getMessage());
}
}
}

View File

@@ -25,10 +25,17 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
private const val TAG = "SubscriptionRepository"
fun Context.isSubscriptionEnabledFlow(subId: Int) = subscriptionsChangedFlow().map {
val subscriptionManager = getSystemService(SubscriptionManager::class.java)
subscriptionManager?.isSubscriptionEnabled(subId) ?: false
}.flowOn(Dispatchers.Default)
fun Context.subscriptionsChangedFlow() = callbackFlow {
val subscriptionManager = getSystemService(SubscriptionManager::class.java)!!

View File

@@ -16,37 +16,33 @@
package com.android.settings.spa.network
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.os.UserManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.euicc.EuiccManager
import android.util.Log
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Message
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.DataUsage
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -59,17 +55,13 @@ import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.SettingsIcon
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf
@@ -85,10 +77,8 @@ import kotlinx.coroutines.withContext
object NetworkCellularGroupProvider : SettingsPageProvider {
override val name = "NetworkCellularGroupProvider"
private lateinit var subscriptionViewModel: SubscriptionInfoListViewModel
private val owner = createSettingsPage()
var selectableSubscriptionInfoList: List<SubscriptionInfo> = listOf()
var defaultVoiceSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var defaultSmsSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var defaultDataSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -106,9 +96,6 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
val context = LocalContext.current
var selectableSubscriptionInfoListRemember = remember {
mutableListOf<SubscriptionInfo>().toMutableStateList()
}
var callsSelectedId = rememberSaveable {
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
}
@@ -122,24 +109,24 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
}
subscriptionViewModel = SubscriptionInfoListViewModel(
context.applicationContext as Application)
val subscriptionViewModel = viewModel<SubscriptionInfoListViewModel>()
allOfFlows(context, subscriptionViewModel.selectableSubscriptionInfoListFlow)
.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
selectableSubscriptionInfoListRemember.clear()
selectableSubscriptionInfoListRemember.addAll(selectableSubscriptionInfoList)
callsSelectedId.intValue = defaultVoiceSubId
textsSelectedId.intValue = defaultSmsSubId
mobileDataSelectedId.intValue = defaultDataSubId
nonDdsRemember.intValue = nonDds
}
remember {
allOfFlows(context, subscriptionViewModel.selectableSubscriptionInfoListFlow)
}.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
callsSelectedId.intValue = defaultVoiceSubId
textsSelectedId.intValue = defaultSmsSubId
mobileDataSelectedId.intValue = defaultDataSubId
nonDdsRemember.intValue = nonDds
}
PageImpl(selectableSubscriptionInfoListRemember,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
nonDdsRemember)
PageImpl(
subscriptionViewModel.selectableSubscriptionInfoListFlow,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
nonDdsRemember
)
}
private fun allOfFlows(context: Context,
@@ -152,13 +139,12 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
NetworkCellularGroupProvider::refreshUiStates,
).flowOn(Dispatchers.Default)
fun refreshUiStates(
inputSelectableSubscriptionInfoList: List<SubscriptionInfo>,
inputDefaultVoiceSubId: Int,
inputDefaultSmsSubId: Int,
inputDefaultDateSubId: Int
): Unit {
selectableSubscriptionInfoList = inputSelectableSubscriptionInfoList
private fun refreshUiStates(
selectableSubscriptionInfoList: List<SubscriptionInfo>,
inputDefaultVoiceSubId: Int,
inputDefaultSmsSubId: Int,
inputDefaultDateSubId: Int
) {
defaultVoiceSubId = inputDefaultVoiceSubId
defaultSmsSubId = inputDefaultSmsSubId
defaultDataSubId = inputDefaultDateSubId
@@ -178,25 +164,23 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
}
@Composable
fun PageImpl(selectableSubscriptionInfoList: List<SubscriptionInfo>,
defaultVoiceSubId: MutableIntState,
defaultSmsSubId: MutableIntState,
defaultDataSubId: MutableIntState,
nonDds: MutableIntState) {
val context = LocalContext.current
var activeSubscriptionInfoList: List<SubscriptionInfo> =
selectableSubscriptionInfoList.filter { subscriptionInfo ->
subscriptionInfo.simSlotIndex != -1
}
var subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
fun PageImpl(
selectableSubscriptionInfoListFlow: StateFlow<List<SubscriptionInfo>>,
defaultVoiceSubId: MutableIntState,
defaultSmsSubId: MutableIntState,
defaultDataSubId: MutableIntState,
nonDds: MutableIntState
) {
val selectableSubscriptionInfoList by selectableSubscriptionInfoListFlow
.collectAsStateWithLifecycle(initialValue = emptyList())
val activeSubscriptionInfoList: List<SubscriptionInfo> =
selectableSubscriptionInfoList.filter { subscriptionInfo ->
subscriptionInfo.simSlotIndex != -1
}
val stringSims = stringResource(R.string.provider_network_settings_title)
RegularScaffold(title = stringSims) {
SimsSectionImpl(
context,
subscriptionManager,
selectableSubscriptionInfoList
)
SimsSection(selectableSubscriptionInfoList)
PrimarySimSectionImpl(
activeSubscriptionInfoList,
defaultVoiceSubId,
@@ -207,56 +191,6 @@ fun PageImpl(selectableSubscriptionInfoList: List<SubscriptionInfo>,
}
}
@Composable
fun SimsSectionImpl(
context: Context,
subscriptionManager: SubscriptionManager?,
subscriptionInfoList: List<SubscriptionInfo>
) {
val coroutineScope = rememberCoroutineScope()
for (subInfo in subscriptionInfoList) {
val checked = rememberSaveable() {
mutableStateOf(false)
}
//TODO: Add the Restricted TwoTargetSwitchPreference in SPA
TwoTargetSwitchPreference(
object : SwitchPreferenceModel {
override val title = subInfo.displayName.toString()
override val summary = { subInfo.number }
override val checked = {
coroutineScope.launch {
withContext(Dispatchers.Default) {
checked.value = subscriptionManager?.isSubscriptionEnabled(
subInfo.subscriptionId)?:false
}
}
checked.value
}
override val onCheckedChange = { newChecked: Boolean ->
startToggleSubscriptionDialog(context, subInfo, newChecked)
}
}
) {
startMobileNetworkSettings(context, subInfo)
}
}
// + add sim
if (showEuiccSettings(context)) {
RestrictedPreference(
model = object : PreferenceModel {
override val title = stringResource(id = R.string.mobile_network_list_add_more)
override val icon = @Composable { SettingsIcon(Icons.Outlined.Add) }
override val onClick = {
startAddSimFlow(context)
}
},
restrictions = Restrictions(keys =
listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
)
}
}
@Composable
fun PrimarySimImpl(
subscriptionInfoList: List<SubscriptionInfo>,
@@ -440,32 +374,6 @@ private fun Context.defaultDefaultDataSubscriptionFlow(): Flow<Int> =
).map { SubscriptionManager.getDefaultDataSubscriptionId() }
.conflate().flowOn(Dispatchers.Default)
private fun startToggleSubscriptionDialog(
context: Context,
subInfo: SubscriptionInfo,
newStatus: Boolean
) {
SubscriptionUtil.startToggleSubscriptionDialogActivity(
context,
subInfo.subscriptionId,
newStatus
)
}
private fun startMobileNetworkSettings(context: Context, subInfo: SubscriptionInfo) {
MobileNetworkUtils.launchMobileNetworkSettings(context, subInfo)
}
private fun startAddSimFlow(context: Context) {
val intent = Intent(EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION)
intent.putExtra(EuiccManager.EXTRA_FORCE_PROVISION, true)
context.startActivity(intent)
}
private fun showEuiccSettings(context: Context): Boolean {
return MobileNetworkUtils.showEuiccSettings(context)
}
suspend fun setDefaultVoice(
subscriptionManager: SubscriptionManager?,
subId: Int

View File

@@ -0,0 +1,98 @@
/*
* 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.spa.network
import android.content.Context
import android.content.Intent
import android.os.UserManager
import android.telephony.SubscriptionInfo
import android.telephony.euicc.EuiccManager
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.isSubscriptionEnabledFlow
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spa.widget.ui.SettingsIcon
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
@Composable
fun SimsSection(subscriptionInfoList: List<SubscriptionInfo>) {
Column {
for (subInfo in subscriptionInfoList) {
SimPreference(subInfo)
}
AddSim()
}
}
@Composable
private fun SimPreference(subInfo: SubscriptionInfo) {
val context = LocalContext.current
val checked = remember(subInfo.subscriptionId) {
context.isSubscriptionEnabledFlow(subInfo.subscriptionId)
}.collectAsStateWithLifecycle(initialValue = false)
//TODO: Add the Restricted TwoTargetSwitchPreference in SPA
TwoTargetSwitchPreference(
object : SwitchPreferenceModel {
override val title = subInfo.displayName.toString()
override val summary = { subInfo.number }
override val checked = { checked.value }
override val onCheckedChange = { newChecked: Boolean ->
SubscriptionUtil.startToggleSubscriptionDialogActivity(
context,
subInfo.subscriptionId,
newChecked,
)
}
}
) {
MobileNetworkUtils.launchMobileNetworkSettings(context, subInfo)
}
}
@Composable
private fun AddSim() {
val context = LocalContext.current
if (remember { MobileNetworkUtils.showEuiccSettings(context) }) {
RestrictedPreference(
model = object : PreferenceModel {
override val title = stringResource(id = R.string.mobile_network_list_add_more)
override val icon = @Composable { SettingsIcon(Icons.Outlined.Add) }
override val onClick = { startAddSimFlow(context) }
},
restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
)
}
}
private fun startAddSimFlow(context: Context) {
val intent = Intent(EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION)
intent.putExtra(EuiccManager.EXTRA_FORCE_PROVISION, true)
context.startActivity(intent)
}

View File

@@ -33,6 +33,7 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class SubscriptionRepositoryTest {
@@ -49,6 +50,17 @@ class SubscriptionRepositoryTest {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
@Test
fun isSubscriptionEnabledFlow() = runBlocking {
mockSubscriptionManager.stub {
on { isSubscriptionEnabled(SUB_ID) } doReturn true
}
val isEnabled = context.isSubscriptionEnabledFlow(SUB_ID).firstWithTimeoutOrNull()
assertThat(isEnabled).isTrue()
}
@Test
fun subscriptionsChangedFlow_hasInitialValue() = runBlocking {
val initialValue = context.subscriptionsChangedFlow().firstWithTimeoutOrNull()
@@ -67,4 +79,8 @@ class SubscriptionRepositoryTest {
assertThat(listDeferred.await()).hasSize(2)
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright (C) 2021 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.development;
import static com.android.settings.development.NfcStackDebugLogPreferenceController
.NFC_STACK_DEBUGLOG_ENABLED_PROPERTY;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.Looper;
import android.os.SystemProperties;
import androidx.preference.SwitchPreference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class NfcStackDebugLogPreferenceControllerTest {
private Context mContext;
private NfcStackDebugLogPreferenceController mController;
private SwitchPreference mPreference;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new NfcStackDebugLogPreferenceController(mContext);
if (Looper.myLooper() == null) {
Looper.prepare();
}
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
mPreference = new SwitchPreference(mContext);
mPreference.setKey(mController.getPreferenceKey());
screen.addPreference(mPreference);
mController.displayPreference(screen);
}
@Test
public void onPreferenceChanged_settingDisabled_shouldTurnOffNfcStackDebugLog() {
mController.onPreferenceChange(mPreference, false /* new value */);
final boolean mode = SystemProperties.getBoolean(
NFC_STACK_DEBUGLOG_ENABLED_PROPERTY, false /* default */);
assertThat(mode).isFalse();
}
@Test
public void onPreferenceChanged_settingEnabled_shouldTurnOnNfcStackDebugLog() {
mController.onPreferenceChange(mPreference, true /* new value */);
final boolean mode = SystemProperties.getBoolean(
NFC_STACK_DEBUGLOG_ENABLED_PROPERTY, false /* default */);
assertThat(mode).isTrue();
}
@Test
public void updateState_settingEnabled_preferenceShouldBeChecked() {
SystemProperties.set(NFC_STACK_DEBUGLOG_ENABLED_PROPERTY,
Boolean.toString(true));
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
SystemProperties.set(NFC_STACK_DEBUGLOG_ENABLED_PROPERTY,
Boolean.toString(false));
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onDeveloperOptionsDisabled_shouldDisablePreference() {
mController.onDeveloperOptionsSwitchDisabled();
final boolean mode = SystemProperties.getBoolean(
NFC_STACK_DEBUGLOG_ENABLED_PROPERTY,
false /* default */);
mController.updateState(mPreference);
assertThat(mode).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
}