From e2c131668270d713d776aca7530ed9c60e4cf8e4 Mon Sep 17 00:00:00 2001 From: hoffc Date: Tue, 3 Dec 2024 15:13:58 +0800 Subject: [PATCH 1/9] Settings: Fix face enroll stack overflow Stack overflow occurs when enrolling face. Buganizer: 381974811 Change-Id: I9c5ae8f07f5ce9174b270abfef313bcf12fd2c57 --- .../android/settings/biometrics/face/FaceEnrollEnrolling.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java index 32d2a11c6c8..d520cd64f43 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java @@ -144,7 +144,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { @Override protected void startEnrollmentInternal() { - super.startEnrollment(); + super.startEnrollmentInternal(); mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager() .findFragmentByTag(TAG_FACE_PREVIEW); if (mPreviewFragment == null) { From 4efa8adfa6ed66429c0d4595efa1368dbe1ee612 Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Wed, 27 Nov 2024 10:37:39 +0000 Subject: [PATCH 2/9] [AAPM] UX for disabling WEP Toggle is restricted, and trying to connect shows an error Bug: 352420507 Change-Id: If20bf030d1c0aba55dac135a9f5f51e9a4255596 Test: Manually. Automated tests to follow Flag: com.android.wifi.flags.wep_disabled_in_apm --- .../wifi/WepNetworksPreferenceController.kt | 90 +++++++++++++++---- .../WepNetworksPreferenceControllerTest.kt | 28 ++++++ 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt index bad7201128c..92716bec4ff 100644 --- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt +++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt @@ -18,6 +18,9 @@ package com.android.settings.wifi import android.content.Context import android.net.wifi.WifiManager +import android.security.advancedprotection.AdvancedProtectionManager +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -27,6 +30,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.style.TextAlign import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R @@ -50,6 +54,9 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : ComposePreferenceController(context, preferenceKey) { var wifiManager = context.getSystemService(WifiManager::class.java)!! + var aapmManager = if (android.security.Flags.aapmApi() && Flags.wepDisabledInApm()) + context.getSystemService(AdvancedProtectionManager::class.java)!! + else null override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE else UNSUPPORTED_ON_DEVICE @@ -60,27 +67,49 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : isWepSupportedFlow.collectAsStateWithLifecycle(initialValue = null).value val isWepAllowed: Boolean? = wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null).value - var openDialog by rememberSaveable { mutableStateOf(false) } - SwitchPreference( - object : SwitchPreferenceModel { - override val title = stringResource(R.string.wifi_allow_wep_networks) - override val summary = { getSummary(isWepSupported) } - override val checked = { - if (isWepSupported == true) isWepAllowed else isWepSupported - } - override val changeable: () -> Boolean - get() = { isWepSupported == true } + val isAapmEnabled: Boolean? = if (android.security.Flags.aapmApi() + && Flags.wepDisabledInApm()) + isAapmEnabledFlow.collectAsStateWithLifecycle(initialValue = null).value + else false - override val onCheckedChange: (Boolean) -> Unit = { newChecked -> - val wifiInfo = wifiManager.connectionInfo - if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) { - openDialog = true - } else { - wifiManager.setWepAllowed(newChecked) - wepAllowedFlow.override(newChecked) + var openDialog by rememberSaveable { mutableStateOf(false) } + + RestrictionWrapper( + restricted = isAapmEnabled == true + ) { + SwitchPreference( + object : SwitchPreferenceModel { + override val title = stringResource(R.string.wifi_allow_wep_networks) + override val summary = { getSummary(isWepSupported) } + override val checked = { + when { + isWepSupported == false -> false + isAapmEnabled == true -> false + else -> isWepAllowed + } } + override val changeable: () -> Boolean + get() = { isWepSupported == true && isAapmEnabled == false } + + override val onCheckedChange: ((Boolean) -> Unit)? = + if (isAapmEnabled == true) { + null + } else { + { newChecked -> + val wifiInfo = wifiManager.connectionInfo + if (!newChecked && + wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP + ) { + openDialog = true + } else { + wifiManager.setWepAllowed(newChecked) + wepAllowedFlow.override(newChecked) + } + } + } } - }) + ) + } if (openDialog) { SettingsAlertDialogWithIcon( onDismissRequest = { openDialog = false }, @@ -103,6 +132,21 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : } } + @Composable + private fun RestrictionWrapper(restricted: Boolean, content: @Composable () -> Unit) { + if (restricted) { + Box( + Modifier.clickable( + enabled = true, + role = Role.Switch, + onClick = ::startSupportIntent + ) + ) { content() } + } else { + content() + } + } + private fun getSummary(isWepSupported: Boolean?): String = mContext.getString( when (isWepSupported) { @@ -114,6 +158,16 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : private val isWepSupportedFlow = flow { emit(wifiManager.isWepSupported) }.flowOn(Dispatchers.Default) + private val isAapmEnabledFlow = flow { + emit(aapmManager?.isAdvancedProtectionEnabled ?: false) }.flowOn(Dispatchers.Default) + + private fun startSupportIntent() { + aapmManager?.createSupportIntent( + AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP, + AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING + )?.let { mContext.startActivity(it) } + } + val wepAllowedFlow = OverridableFlow( callbackFlow { diff --git a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt index 9183096fd7b..1f0a4916310 100644 --- a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt @@ -17,8 +17,10 @@ package com.android.settings.wifi import android.content.Context +import android.content.Intent import android.net.wifi.WifiInfo import android.net.wifi.WifiManager +import android.security.advancedprotection.AdvancedProtectionManager import androidx.compose.ui.test.assertIsOff import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.isDisplayed @@ -43,9 +45,12 @@ import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doNothing import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.stub +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class WepNetworksPreferenceControllerTest { @@ -71,9 +76,15 @@ class WepNetworksPreferenceControllerTest { on { connectionInfo } doReturn mockWifiInfo } + private var mockAapmManager = + mock { + on { isAdvancedProtectionEnabled } doReturn false + } + private var context: Context = spy(ApplicationProvider.getApplicationContext()) { on { getSystemService(WifiManager::class.java) } doReturn mockWifiManager + on { getSystemService(AdvancedProtectionManager::class.java) } doReturn mockAapmManager } private var controller = WepNetworksPreferenceController(context, TEST_KEY) @@ -185,6 +196,23 @@ class WepNetworksPreferenceControllerTest { .isNotDisplayed() } + @Test + fun whenClick_aapmEnabled_openDialog() { + mockAapmManager.stub { + on { isAdvancedProtectionEnabled } doReturn true + on { createSupportIntent(any(), any()) } doReturn Intent() + } + doNothing().whenever(context).startActivity(any()) + composeTestRule.setContent { controller.Content() } + + composeTestRule.onRoot().performClick() + + composeTestRule + .onDialogText(context.getString(R.string.wifi_disconnect_button_text)) + .isNotDisplayed() + verify(context).startActivity(any()) + } + private companion object { const val TEST_KEY = "test_key" const val SSID = "ssid" From e0f75b626b642eae3abc15a092602056ea6e5807 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Mon, 9 Dec 2024 19:21:59 +0000 Subject: [PATCH 3/9] Updated initial value for accessibility interactor Test: Verified the default value is correct Bug: 383060578 Flag: EXEMPT bugfix Change-Id: I205609165a746d9d155eb6a3b91c89d22a97ed9f --- .../fingerprint2/domain/interactor/AccessibilityInteractor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt index bf0084d14a8..52a32811ad6 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt @@ -54,7 +54,7 @@ class AccessibilityInteractorImpl(private val accessibilityManager: Accessibilit .stateIn( scope, SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener - false, + accessibilityManager.isEnabled, ) override val isEnabled: Boolean From bc26a1decc0bb2791743abd54a6cb094e94ffd12 Mon Sep 17 00:00:00 2001 From: Jason Chang Date: Tue, 10 Dec 2024 07:58:16 +0000 Subject: [PATCH 4/9] Change resource id to the correct package name Change resource id to the correct SUW package name. Flag: EXEMPT bug-fixing Bug: 383030121 Test: Build ABTD manually then check the UI Change-Id: Ic7dce3c0f61abf56e49163240d12ac5da8b627c6 --- src/com/android/settings/password/PasswordUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java index 8c8afc2e9ef..da2ad6331e4 100644 --- a/src/com/android/settings/password/PasswordUtils.java +++ b/src/com/android/settings/password/PasswordUtils.java @@ -109,7 +109,8 @@ public final class PasswordUtils extends com.android.settingslib.Utils { public static void setupScreenLockOptionsButton(Context context, View view, Button optButton) { final LinearLayout headerLayout = view.findViewById( com.google.android.setupdesign.R.id.sud_layout_header); - final TextView sucTitleView = headerLayout.findViewById(R.id.suc_layout_title); + final TextView sucTitleView = headerLayout.findViewById( + com.google.android.setupdesign.R.id.suc_layout_title); if (headerLayout != null && sucTitleView != null) { final ViewGroup.MarginLayoutParams layoutTitleParams = (ViewGroup.MarginLayoutParams) sucTitleView.getLayoutParams(); From c9c088ae34b2e0a1f2be4feeee6e96a44cdcbd6b Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Mon, 9 Dec 2024 12:01:49 +0800 Subject: [PATCH 5/9] [Audiosharing] Check profile readiness before adding source Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Bug: 362858921 Change-Id: I9ee6226d1bd16225adf1756678ef7565941d7c60 --- .../BluetoothDevicePairingDetailBase.java | 43 +++++++++++++++++-- .../BluetoothDevicePairingDetailBaseTest.java | 3 ++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java index 36a14aa54fc..33eba1074c4 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java @@ -48,6 +48,8 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.HearingAidStatsLogUtils; import com.android.settingslib.utils.ThreadUtils; +import com.google.common.collect.ImmutableList; + import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,8 +60,11 @@ import java.util.concurrent.TimeUnit; * device pairing detail page. */ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment { - private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10); + private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15); private static final int AUTO_DISMISS_MESSAGE_ID = 1001; + private static final ImmutableList AUDIO_SHARING_PROFILES = ImmutableList.of( + BluetoothProfile.LE_AUDIO, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.VOLUME_CONTROL); protected boolean mInitialScanStarted; @VisibleForTesting @@ -229,12 +234,13 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere if (device != null && mSelectedList.contains(device)) { if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) { - if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT + if (mShouldTriggerAudioSharingShareThenPairFlow && state == BluetoothAdapter.STATE_CONNECTED && device.equals(mJustBonded) - && mShouldTriggerAudioSharingShareThenPairFlow) { + && AUDIO_SHARING_PROFILES.contains(bluetoothProfile) + && isReadyForAudioSharing(cachedDevice, bluetoothProfile)) { Log.d(getLogTag(), - "onProfileConnectionStateChanged, assistant profile connected"); + "onProfileConnectionStateChanged, ready for audio sharing"); dismissConnectingDialog(); mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID); finishFragmentWithResultForAudioSharing(device); @@ -322,6 +328,35 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere return false; } + private boolean isReadyForAudioSharing(@NonNull CachedBluetoothDevice cachedDevice, + int justConnectedProfile) { + for (int profile : AUDIO_SHARING_PROFILES) { + // Skip checking connection state for just connected profile + if (profile == justConnectedProfile) continue; + switch (profile) { + case BluetoothProfile.LE_AUDIO -> { + if (!cachedDevice.isConnectedLeAudioDevice()) { + Log.d(getLogTag(), "isReadyForAudioSharing, LE_AUDIO not ready"); + return false; + } + } + case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT -> { + if (!cachedDevice.isConnectedLeAudioBroadcastAssistantDevice()) { + Log.d(getLogTag(), "isReadyForAudioSharing, ASSISTANT not ready"); + return false; + } + } + case BluetoothProfile.VOLUME_CONTROL -> { + if (!cachedDevice.isConnectedVolumeControlDevice()) { + Log.d(getLogTag(), "isReadyForAudioSharing, VC not ready"); + return false; + } + } + } + } + return true; + } + private void addOnMetadataChangedListener(@Nullable BluetoothDevice device) { var unused = ThreadUtils.postOnBackgroundThread(() -> { if (mBluetoothAdapter != null && device != null diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java index 949b3d83809..bd6ac4bf2c0 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java @@ -308,6 +308,9 @@ public class BluetoothDevicePairingDetailBaseTest { shadowOf(Looper.getMainLooper()).idle(); when(mCachedBluetoothDevice.isConnected()).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedLeAudioBroadcastAssistantDevice()).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedVolumeControlDevice()).thenReturn(true); mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); From 2043c1e7d87f7e784f75d6b936cc89765bd72e15 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 11 Dec 2024 10:04:38 +0800 Subject: [PATCH 6/9] [Catalyst] Remove UserManager.addUserRestrictionsListener usage Bug: 377600992 Bug: 379130874 Flag: com.android.settings.flags.catalyst Test: testdpc Change-Id: If0111a19af7a077a0a06c4a4fa7f1e555fbed3b9 --- .../settings/SettingsPreferenceFragment.java | 8 -- .../UserRestrictionBindingHelper.kt | 32 ++++---- .../settings/restriction/UserRestrictions.kt | 78 ++++++++----------- 3 files changed, 51 insertions(+), 67 deletions(-) diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index 363d601d0d1..f272c1da9d8 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -17,7 +17,6 @@ package com.android.settings; import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; -import static com.android.settingslib.media.PhoneMediaDevice.isDesktop; import android.app.Activity; import android.app.Dialog; @@ -187,13 +186,6 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF /** Returns if catalyst is enabled on current screen. */ public final boolean isCatalystEnabled() { - // TODO(b/379130874): make Catalyst compatible with desktop device, such as user restriction - // check. - Context context = getContext(); - if (context != null && isDesktop(context)) { - return false; - } - return getPreferenceScreenCreator() != null; } diff --git a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt index 16104de782f..a1329c2ecdf 100644 --- a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt +++ b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt @@ -25,9 +25,9 @@ import com.android.settingslib.preference.PreferenceScreenBindingHelper.Companio /** Helper to rebind preference immediately when user restriction is changed. */ class UserRestrictionBindingHelper( - context: Context, + private val context: Context, private val screenBindingHelper: PreferenceScreenBindingHelper, -) : AutoCloseable { +) : KeyedObserver, AutoCloseable { private val restrictionKeysToPreferenceKeys: Map> = mutableMapOf>() .apply { @@ -42,27 +42,29 @@ class UserRestrictionBindingHelper( } .toMap() - private val userRestrictionObserver: KeyedObserver? - init { - if (restrictionKeysToPreferenceKeys.isEmpty()) { - userRestrictionObserver = null - } else { - val observer = - KeyedObserver { restrictionKey, _ -> - restrictionKey?.let { notifyRestrictionChanged(it) } - } - UserRestrictions.addObserver(context, observer, HandlerExecutor.main) - userRestrictionObserver = observer + val restrictionKeys = restrictionKeysToPreferenceKeys.keys + if (restrictionKeys.isNotEmpty()) { + val userRestrictions = UserRestrictions.get(context) + val executor = HandlerExecutor.main + for (restrictionKey in restrictionKeys) { + userRestrictions.addObserver(restrictionKey, this, executor) + } } } - private fun notifyRestrictionChanged(restrictionKey: String) { + override fun onKeyChanged(restrictionKey: String, reason: Int) { val keys = restrictionKeysToPreferenceKeys[restrictionKey] ?: return for (key in keys) screenBindingHelper.notifyChange(key, CHANGE_REASON_STATE) } override fun close() { - userRestrictionObserver?.let { UserRestrictions.removeObserver(it) } + val restrictionKeys = restrictionKeysToPreferenceKeys.keys + if (restrictionKeys.isNotEmpty()) { + val userRestrictions = UserRestrictions.get(context) + for (restrictionKey in restrictionKeys) { + userRestrictions.removeObserver(restrictionKey, this) + } + } } } diff --git a/src/com/android/settings/restriction/UserRestrictions.kt b/src/com/android/settings/restriction/UserRestrictions.kt index 1fa68307a4a..880aa5d9991 100644 --- a/src/com/android/settings/restriction/UserRestrictions.kt +++ b/src/com/android/settings/restriction/UserRestrictions.kt @@ -16,68 +16,58 @@ package com.android.settings.restriction +import android.content.BroadcastReceiver import android.content.Context -import android.os.Bundle -import android.os.IUserRestrictionsListener +import android.content.Intent +import android.content.IntentFilter import android.os.UserManager -import com.android.settingslib.datastore.KeyedDataObservable +import com.android.settingslib.datastore.AbstractKeyedDataObservable +import com.android.settingslib.datastore.DataChangeReason import com.android.settingslib.datastore.KeyedObserver import java.util.concurrent.Executor -import java.util.concurrent.atomic.AtomicBoolean /** Helper class to monitor user restriction changes. */ -object UserRestrictions { - private val observable = KeyedDataObservable() +class UserRestrictions private constructor(private val applicationContext: Context) { - private val userRestrictionsListener = - object : IUserRestrictionsListener.Stub() { - override fun onUserRestrictionsChanged( - userId: Int, - newRestrictions: Bundle, - prevRestrictions: Bundle, - ) { - // there is no API to remove listener, do a quick check to avoid unnecessary work - if (!observable.hasAnyObserver()) return + private val observable = + object : AbstractKeyedDataObservable() { + override fun onFirstObserverAdded() { + val intentFilter = IntentFilter() + intentFilter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED) + applicationContext.registerReceiver(broadcastReceiver, intentFilter) + } - val changedKeys = mutableSetOf() - val keys = newRestrictions.keySet() + prevRestrictions.keySet() - for (key in keys) { - if (newRestrictions.getBoolean(key) != prevRestrictions.getBoolean(key)) { - changedKeys.add(key) - } - } - - for (key in changedKeys) observable.notifyChange(key, 0) + override fun onLastObserverRemoved() { + applicationContext.unregisterReceiver(broadcastReceiver) } } - private val listenerAdded = AtomicBoolean() + private val broadcastReceiver: BroadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + // there is no way to get the changed keys, just notify all observers + observable.notifyChange(DataChangeReason.UPDATE) + } + } - fun addObserver(context: Context, observer: KeyedObserver, executor: Executor) { - context.addUserRestrictionsListener() + fun addObserver(observer: KeyedObserver, executor: Executor) = observable.addObserver(observer, executor) - } - fun addObserver( - context: Context, - key: String, - observer: KeyedObserver, - executor: Executor, - ) { - context.addUserRestrictionsListener() + fun addObserver(key: String, observer: KeyedObserver, executor: Executor) = observable.addObserver(key, observer, executor) - } - - private fun Context.addUserRestrictionsListener() { - if (listenerAdded.getAndSet(true)) return - // surprisingly, there is no way to remove the listener - applicationContext - .getSystemService(UserManager::class.java) - .addUserRestrictionsListener(userRestrictionsListener) - } fun removeObserver(observer: KeyedObserver) = observable.removeObserver(observer) fun removeObserver(key: String, observer: KeyedObserver) = observable.removeObserver(key, observer) + + companion object { + @Volatile private var instance: UserRestrictions? = null + + fun get(context: Context) = + instance + ?: synchronized(this) { + instance ?: UserRestrictions(context.applicationContext).also { instance = it } + } + } } From 2c5682719f680d1728e6da337d8136ef72497d1a Mon Sep 17 00:00:00 2001 From: tomhsu Date: Wed, 11 Dec 2024 03:50:02 +0000 Subject: [PATCH 7/9] Mock method to avoid exception. - TelephonyManager#getVoiceNetworkType() Flag: EXEMPT bug fix Fix: 383209846 Test: atest pass Change-Id: Iedb971b58cea6ff5844eb4268a53b0d8f0d41062 --- .../deviceinfo/simstatus/SimStatusDialogControllerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java index 3fa380828b9..29e0c1c4851 100644 --- a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java +++ b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java @@ -116,6 +116,7 @@ public class SimStatusDialogControllerTest { doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId( anyInt()); doReturn(2).when(mTelephonyManager).getCardIdForDefaultEuicc(); + doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mTelephonyManager).getVoiceNetworkType(); doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mTelephonyManager).getDataNetworkType(); mUpdatePhoneNumberCount = new AtomicInteger(); From a55788d3b9d21ad84b9dbf51790c903626c4200a Mon Sep 17 00:00:00 2001 From: Jason Chang Date: Wed, 11 Dec 2024 08:10:17 +0000 Subject: [PATCH 8/9] Change resource id to the correct package name Change resource id to the correct SUW package name. Flag: EXEMPT bug-fixing Bug: 383030121 Test: Build and manually then check the UI Change-Id: I31c8c7e79a65edd7953080a9bd16b638a901d607 --- src/com/android/settings/password/ChooseLockPassword.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index eb7d66708b6..80f73b95c66 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -691,7 +691,8 @@ public class ChooseLockPassword extends SettingsActivity { return; } - final TextView sucTitleView = view.findViewById(R.id.suc_layout_title); + final TextView sucTitleView = view.findViewById( + com.google.android.setupdesign.R.id.suc_layout_title); final ViewGroup.MarginLayoutParams titleLayoutParams = (ViewGroup.MarginLayoutParams) sucTitleView.getLayoutParams(); mPasswordRestrictionView = new RecyclerView(getActivity()); From dc80bddc1ef9f26db9ba37d52f3d76f2af7edf9b Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 11 Dec 2024 16:22:49 +0800 Subject: [PATCH 9/9] [Catalyst] Partially revert ag/30664694 The preference is not yet migrated, we should not update fragment based on current flag rollout plan. Bug: 368355361 Bug: 368359963 Flag: com.android.settings.flags.catalyst_tether_settings Test: manual Change-Id: I89a5d2ee29e1c3428d40428b3d3d4ba3afcd4ece --- .../network/tether/TetherSettings.java | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/network/tether/TetherSettings.java b/src/com/android/settings/network/tether/TetherSettings.java index 57715be94db..53de9f65bed 100644 --- a/src/com/android/settings/network/tether/TetherSettings.java +++ b/src/com/android/settings/network/tether/TetherSettings.java @@ -72,7 +72,6 @@ import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -// LINT.IfChange /** * Displays preferences for Tethering. */ @@ -209,19 +208,16 @@ public class TetherSettings extends RestrictedDashboardFragment mWifiTetherPreferenceController.displayPreference(getPreferenceScreen()); } - if (!isCatalystEnabled()) { - if (!bluetoothAvailable) { - mBluetoothTether.setVisible(false); + if (!bluetoothAvailable) { + getPreferenceScreen().removePreference(mBluetoothTether); + } else { + BluetoothPan pan = mBluetoothPan.get(); + if (pan != null && pan.isTetheringOn()) { + mBluetoothTether.setChecked(true); } else { - BluetoothPan pan = mBluetoothPan.get(); - if (pan != null && pan.isTetheringOn()) { - mBluetoothTether.setChecked(true); - } else { - mBluetoothTether.setChecked(false); - } + mBluetoothTether.setChecked(false); } } - if (!ethernetAvailable) getPreferenceScreen().removePreference(mEthernetTether); // Set initial state based on Data Saver mode. onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled()); @@ -274,9 +270,7 @@ public class TetherSettings extends RestrictedDashboardFragment mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled); } mUsbTether.setEnabled(!mDataSaverEnabled); - if (!isCatalystEnabled()) { - mBluetoothTether.setEnabled(!mDataSaverEnabled); - } + mBluetoothTether.setEnabled(!mDataSaverEnabled); mEthernetTether.setEnabled(!mDataSaverEnabled); mDataSaverFooter.setVisible(mDataSaverEnabled); } @@ -526,8 +520,6 @@ public class TetherSettings extends RestrictedDashboardFragment } private void updateBluetoothState() { - if (isCatalystEnabled()) return; - final int btState = getBluetoothState(); if (DEBUG) { Log.d(TAG, "updateBluetoothState() btState : " + btState); @@ -583,7 +575,7 @@ public class TetherSettings extends RestrictedDashboardFragment } private void startTethering(int choice) { - if (choice == TETHERING_BLUETOOTH && !isCatalystEnabled()) { + if (choice == TETHERING_BLUETOOTH) { // Turn on Bluetooth first. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter.getState() == BluetoothAdapter.STATE_OFF) { @@ -605,7 +597,7 @@ public class TetherSettings extends RestrictedDashboardFragment } else { mCm.stopTethering(TETHERING_USB); } - } else if (preference == mBluetoothTether && !isCatalystEnabled()) { + } else if (preference == mBluetoothTether) { if (mBluetoothTether.isChecked()) { startTethering(TETHERING_BLUETOOTH); } else { @@ -753,4 +745,3 @@ public class TetherSettings extends RestrictedDashboardFragment return TetherScreen.KEY; } } -// LINT.ThenChange(BluetoothTetherSwitchPreference.kt)