From 42e46e01050fb68eec1a066ae6f7e4fa6fc30d8c Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Wed, 31 Jul 2024 18:12:22 +0000 Subject: [PATCH 01/11] Make choose lock screen consistent Bug: 237347124 Test: CTS Verifier > Security > Set New word Complexity Flag: EXEMPT bugfix Change-Id: I9437dbd00c435a9f7712db48d783032583e8d090 --- src/com/android/settings/password/SetNewPasswordActivity.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java index 0ba52eab132..36756f91081 100644 --- a/src/com/android/settings/password/SetNewPasswordActivity.java +++ b/src/com/android/settings/password/SetNewPasswordActivity.java @@ -124,9 +124,7 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo @Override public void launchChooseLock(Bundle chooseLockFingerprintExtras) { - final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent()); - Intent intent = isInSetupWizard ? new Intent(this, SetupChooseLockGeneric.class) - : new Intent(this, ChooseLockGeneric.class); + final Intent intent = new Intent(this, SetupChooseLockGeneric.class); intent.setAction(mNewPasswordAction); intent.putExtras(chooseLockFingerprintExtras); intent.putExtra(EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE, From c05f05808366c9177ee955528791c442d328cb71 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 6 Aug 2024 14:15:40 +0100 Subject: [PATCH 02/11] SettingsApp Implement DISALLOW_GRANT_ADMIN in user creation flow In the process of creating a user through the Settings app, if the current user has the DISALLOW_GRANT_ADMIN restriction, the dialogue for granting ADMIN privileges will be hidden after this implementation. This action ensures that even if child users can create SECONDARY users, they cannot create ADMIN users. Design doc: go/unicorn-hsum Bug: 357819541 Test: manually verified the user is not allowed to create ADMIN user when DISALLOW_GRANT_ADMIN restriction is applied. Flag: android.multiuser.unicorn_mode_refactoring_for_hsum_read_only Change-Id: I4b40f83b50d4de0af103d4f5ca400e42345144e0 --- src/com/android/settings/users/UserSettings.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 0cf01e311e1..c387d9e461d 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -919,7 +919,7 @@ public class UserSettings extends SettingsPreferenceFragment d = mCreateUserDialogController.createDialog( getActivity(), this::startActivityForResult, - UserManager.isMultipleAdminEnabled(), + canCreateAdminUser(), (userName, userIcon, isAdmin) -> { mPendingUserIcon = userIcon; mPendingUserName = userName; @@ -937,6 +937,19 @@ public class UserSettings extends SettingsPreferenceFragment return d; } + /** + * Checks if the creation of a new admin user is allowed. + * @return {@code true} if creating a new admin is allowed, {@code false} otherwise. + */ + private boolean canCreateAdminUser() { + if (Flags.unicornModeRefactoringForHsumReadOnly()) { + return UserManager.isMultipleAdminEnabled() + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN); + } else { + return UserManager.isMultipleAdminEnabled(); + } + } + @Override public int getDialogMetricsCategory(int dialogId) { switch (dialogId) { From da812758f3ca7bd90e86f430af63afe58c947ce7 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Fri, 26 Jul 2024 17:26:24 +0800 Subject: [PATCH 03/11] [Audiosharing] Update dialog btn when start sharing with no extra connected device There are two btns now: Pair new device and Show QR code Pair new device -> go to Pair new device page Show QR code -> go to QR code page Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Bug: 305620450 Change-Id: Id5ddb66f1bbc3b02228a0d639bf30cdc40bef265 --- .../dialog_custom_body_audio_sharing.xml | 1 + .../AudioSharingDialogFragment.java | 53 +++++++++--- .../AudioSharingDialogHandler.java | 2 +- .../AudioSharingDialogFragmentTest.java | 80 +++++++++++++------ 4 files changed, 98 insertions(+), 38 deletions(-) diff --git a/res/layout/dialog_custom_body_audio_sharing.xml b/res/layout/dialog_custom_body_audio_sharing.xml index 528bfbb43a7..ba7f6431baa 100644 --- a/res/layout/dialog_custom_body_audio_sharing.xml +++ b/res/layout/dialog_custom_body_audio_sharing.xml @@ -44,6 +44,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:contentDescription="@null" + android:paddingBottom="24dp" android:visibility="gone" /> onCancelClick()); + .setCustomPositiveButton( + R.string.audio_sharing_pair_button_label, + v -> { + dismiss(); + new SubSettingLauncher(getContext()) + .setDestination(BluetoothPairingDetail.class.getName()) + .setSourceMetricsCategory(getMetricsCategory()) + .launch(); + logDialogPositiveBtnClick(); + }) + .setCustomNegativeButton( + R.string.audio_sharing_qrcode_button_label, + v -> { + dismiss(); + new SubSettingLauncher(getContext()) + .setTitleRes(R.string.audio_streams_qr_code_page_title) + .setDestination(AudioStreamsQrCodeFragment.class.getName()) + .setSourceMetricsCategory(getMetricsCategory()) + .launch(); + logDialogNegativeBtnClick(); + }); } else if (deviceItems.size() == 1) { AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems); builder.setTitle( @@ -145,11 +166,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { v -> { if (sListener != null) { sListener.onItemClick(deviceItem); - mMetricsFeatureProvider.action( - getContext(), - SettingsEnums - .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, - sEventData); + logDialogPositiveBtnClick(); } dismiss(); }) @@ -165,6 +182,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { (AudioSharingDeviceItem item) -> { if (sListener != null) { sListener.onItemClick(item); + logDialogPositiveBtnClick(); } dismiss(); }, @@ -178,11 +196,22 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { private void onCancelClick() { if (sListener != null) { sListener.onCancelClick(); - mMetricsFeatureProvider.action( - getContext(), - SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, - sEventData); + logDialogNegativeBtnClick(); } dismiss(); } + + private void logDialogPositiveBtnClick() { + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, + sEventData); + } + + private void logDialogNegativeBtnClick() { + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, + sEventData); + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java index 4ee405d0f35..14c19decb6f 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java @@ -499,7 +499,7 @@ public class AudioSharingDialogHandler { private void removeSourceForGroup( int groupId, Map> groupedDevices) { if (mAssistant == null) { - Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId); + Log.d(TAG, "Fail to remove source due to null profiles, group = " + groupId); return; } if (!groupedDevices.containsKey(groupId)) { diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java index 20c225c09bf..7227f37998b 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java @@ -93,7 +93,6 @@ public class AudioSharingDialogFragmentTest { new Pair[] {TEST_EVENT_DATA}; private Fragment mParent; - private AudioSharingDialogFragment mFragment; private FakeFeatureFactory mFeatureFactory; @Before @@ -107,7 +106,6 @@ public class AudioSharingDialogFragmentTest { shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); mFeatureFactory = FakeFeatureFactory.setupForTest(); - mFragment = new AudioSharingDialogFragment(); mParent = new Fragment(); FragmentController.setupFragment( mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); @@ -120,7 +118,8 @@ public class AudioSharingDialogFragmentTest { @Test public void getMetricsCategory_correctValue() { - assertThat(mFragment.getMetricsCategory()) + AudioSharingDialogFragment fragment = new AudioSharingDialogFragment(); + assertThat(fragment.getMetricsCategory()) .isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE); } @@ -145,7 +144,7 @@ public class AudioSharingDialogFragmentTest { } @Test - public void onCreateDialog_flagOn_noConnectedDevice() { + public void onCreateDialog_flagOn_noExtraConnectedDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); AudioSharingDialogFragment.show( mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); @@ -157,42 +156,67 @@ public class AudioSharingDialogFragmentTest { assertThat(description).isNotNull(); ImageView image = dialog.findViewById(R.id.description_image); assertThat(image).isNotNull(); - Button shareBtn = dialog.findViewById(R.id.positive_btn); - assertThat(shareBtn).isNotNull(); - Button cancelBtn = dialog.findViewById(R.id.negative_btn); - assertThat(cancelBtn).isNotNull(); + Button positiveBtn = dialog.findViewById(R.id.positive_btn); + assertThat(positiveBtn).isNotNull(); + Button negativeBtn = dialog.findViewById(R.id.negative_btn); + assertThat(negativeBtn).isNotNull(); assertThat(dialog.isShowing()).isTrue(); assertThat(description.getVisibility()).isEqualTo(View.VISIBLE); assertThat(description.getText().toString()) .isEqualTo(mParent.getString(R.string.audio_sharing_dialog_connect_device_content)); assertThat(image.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE); - assertThat(cancelBtn.getVisibility()).isEqualTo(View.GONE); + assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(positiveBtn.getText().toString()) + .isEqualTo(mParent.getString(R.string.audio_sharing_pair_button_label)); + assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(negativeBtn.getText().toString()) + .isEqualTo(mParent.getString(R.string.audio_sharing_qrcode_button_label)); } @Test - public void onCreateDialog_noConnectedDevice_dialogDismiss() { + public void onCreateDialog_noExtraConnectedDevice_pairNewDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); AudioSharingDialogFragment.show( mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); - View btnView = dialog.findViewById(android.R.id.button2); - assertThat(btnView).isNotNull(); - btnView.performClick(); + Button pairBtn = dialog.findViewById(R.id.positive_btn); + assertThat(pairBtn).isNotNull(); + pairBtn.performClick(); shadowMainLooper().idle(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); assertThat(dialog.isShowing()).isFalse(); + } + + @Test + public void onCreateDialog_noExtraConnectedDevice_showQRCode() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + AudioSharingDialogFragment.show( + mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); + shadowMainLooper().idle(); + AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + Button qrCodeBtn = dialog.findViewById(R.id.negative_btn); + assertThat(qrCodeBtn).isNotNull(); + qrCodeBtn.performClick(); + shadowMainLooper().idle(); + verify(mFeatureFactory.metricsFeatureProvider) .action( any(Context.class), eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), eq(TEST_EVENT_DATA)); + assertThat(dialog.isShowing()).isFalse(); } @Test - public void onCreateDialog_flagOn_singleConnectedDevice() { + public void onCreateDialog_flagOn_singleExtraConnectedDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); @@ -207,10 +231,10 @@ public class AudioSharingDialogFragmentTest { assertThat(description).isNotNull(); ImageView image = dialog.findViewById(R.id.description_image); assertThat(image).isNotNull(); - Button shareBtn = dialog.findViewById(R.id.positive_btn); - assertThat(shareBtn).isNotNull(); - Button cancelBtn = dialog.findViewById(R.id.negative_btn); - assertThat(cancelBtn).isNotNull(); + Button positiveBtn = dialog.findViewById(R.id.positive_btn); + assertThat(positiveBtn).isNotNull(); + Button negativeBtn = dialog.findViewById(R.id.negative_btn); + assertThat(negativeBtn).isNotNull(); assertThat(dialog.isShowing()).isTrue(); assertThat(title.getText().toString()) .isEqualTo( @@ -220,12 +244,16 @@ public class AudioSharingDialogFragmentTest { assertThat(description.getText().toString()) .isEqualTo(mParent.getString(R.string.audio_sharing_dialog_share_content)); assertThat(image.getVisibility()).isEqualTo(View.GONE); - assertThat(shareBtn.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(positiveBtn.getText().toString()) + .isEqualTo(mParent.getString(R.string.audio_sharing_share_button_label)); + assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(negativeBtn.getText().toString()) + .isEqualTo(mParent.getString(R.string.audio_sharing_no_thanks_button_label)); } @Test - public void onCreateDialog_singleConnectedDevice_dialogDismiss() { + public void onCreateDialog_singleExtraConnectedDevice_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); @@ -248,7 +276,7 @@ public class AudioSharingDialogFragmentTest { } @Test - public void onCreateDialog_singleConnectedDevice_shareClicked() { + public void onCreateDialog_singleExtraConnectedDevice_shareClicked() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); @@ -285,7 +313,7 @@ public class AudioSharingDialogFragmentTest { } @Test - public void onCreateDialog_flagOn_multipleConnectedDevice() { + public void onCreateDialog_flagOn_multipleExtraConnectedDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); @@ -313,12 +341,14 @@ public class AudioSharingDialogFragmentTest { assertThat(image.getVisibility()).isEqualTo(View.GONE); assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE); assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(cancelBtn.getText().toString()) + .isEqualTo(mParent.getString(com.android.settings.R.string.cancel)); assertThat(recyclerView.getVisibility()).isEqualTo(View.VISIBLE); assertThat(recyclerView.getAdapter().getItemCount()).isEqualTo(3); } @Test - public void onCreateDialog_multipleConnectedDevice_dialogDismiss() { + public void onCreateDialog_multipleExtraConnectedDevice_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); From 82bd2c75f7a658a9ed034bc3697720279c079da0 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Wed, 29 May 2024 11:46:46 +0000 Subject: [PATCH 04/11] Fix NPE in date time settings - the callback is null when slice call setChecked() of PreferenceController Bug: 343109816 Bug: 334924107 Test: robotest & manual Flag: EXEMPT bug fix Change-Id: Ia4d60455b6f14a46d00ae285017bc96ad4855679 --- .../settings/datetime/AutoTimePreferenceController.java | 5 +++++ .../settings/datetime/AutoTimeZonePreferenceController.java | 5 +++++ .../settings/datetime/TimeFormatPreferenceController.java | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/com/android/settings/datetime/AutoTimePreferenceController.java b/src/com/android/settings/datetime/AutoTimePreferenceController.java index 434eba9651e..2942acb1956 100644 --- a/src/com/android/settings/datetime/AutoTimePreferenceController.java +++ b/src/com/android/settings/datetime/AutoTimePreferenceController.java @@ -39,6 +39,11 @@ public class AutoTimePreferenceController extends TogglePreferenceController { public AutoTimePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mTimeManager = context.getSystemService(TimeManager.class); + // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when + // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the + // controller is instantiated outside of the context of the real Date & Time settings + // screen. + mCallback = (c) -> {}; } public void setDateAndTimeCallback(UpdateTimeAndDateCallback callback) { diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java index 011cc9717cf..3a1f995aaf2 100644 --- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java +++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java @@ -40,6 +40,11 @@ public class AutoTimeZonePreferenceController extends TogglePreferenceController public AutoTimeZonePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mTimeManager = context.getSystemService(TimeManager.class); + // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when + // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the + // controller is instantiated outside of the context of the real Date & Time settings + // screen. + mCallback = (c) -> {}; } /** diff --git a/src/com/android/settings/datetime/TimeFormatPreferenceController.java b/src/com/android/settings/datetime/TimeFormatPreferenceController.java index 22f7509fb7f..19805ad03dd 100644 --- a/src/com/android/settings/datetime/TimeFormatPreferenceController.java +++ b/src/com/android/settings/datetime/TimeFormatPreferenceController.java @@ -43,6 +43,11 @@ public class TimeFormatPreferenceController extends TogglePreferenceController { public TimeFormatPreferenceController(Context context, String key) { super(context, key); mDummyDate = Calendar.getInstance(); + // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when + // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the + // controller is instantiated outside of the context of the real Date & Time settings + // screen. + mUpdateTimeAndDateCallback = (c) -> {}; } /** From 749c22b1c6fdf76d62b61af77f495e9520c57677 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 8 Aug 2024 13:46:43 +0800 Subject: [PATCH 05/11] Refactor MobileNetworkSettingsSearchIndex To support the case that MobileNetworkSettingsSearchItem could have different key or title. Bug: 358238959 Flag: EXEMPT refactor Test: manual - search vo5g Test: atest MobileNetworkSettingsSearchIndexTest Test: atest MmsMessagePreferenceControllerTest Change-Id: I18afc8a347566d50dd7969d2c906d0ae64be8a80 --- .../MmsMessagePreferenceController.kt | 18 ++++++++--- .../MobileNetworkSettingsSearchIndex.kt | 31 ++++++++++--------- .../NrAdvancedCallingPreferenceController.kt | 16 +++++++--- .../telephony/RoamingPreferenceController.kt | 16 +++++++--- .../WifiCallingPreferenceController.kt | 14 ++++++--- .../MobileNetworkSettingsSearchIndexTest.kt | 11 ++++--- 6 files changed, 68 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt index c929d5ca897..220218c9473 100644 --- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt +++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt @@ -20,11 +20,13 @@ import android.content.Context import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.data.ApnSetting +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceScreen import com.android.settings.R import com.android.settings.Settings.MobileNetworkActivity.EXTRA_MMS_MESSAGE import com.android.settings.core.TogglePreferenceController +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.flow.combine @@ -109,7 +111,7 @@ constructor( } class MmsMessageSearchItem( - context: Context, + private val context: Context, private val getDefaultDataSubId: () -> Int = { SubscriptionManager.getDefaultDataSubscriptionId() }, @@ -117,12 +119,18 @@ constructor( private var telephonyManager: TelephonyManager = context.getSystemService(TelephonyManager::class.java)!! - override val key: String = EXTRA_MMS_MESSAGE - override val title: String = context.getString(R.string.mms_message_title) - - override fun isAvailable(subId: Int): Boolean = + @VisibleForTesting + fun isAvailable(subId: Int): Boolean = getAvailabilityStatus( telephonyManager.createForSubscriptionId(subId), subId, getDefaultDataSubId) + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = EXTRA_MMS_MESSAGE, + title = context.getString(R.string.mms_message_title), + ) + } } } } diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index 4e97d318033..b702054643e 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -39,15 +39,14 @@ class MobileNetworkSettingsSearchIndex( private val searchItemsFactory: (context: Context) -> List = ::createSearchItems, ) { + data class MobileNetworkSettingsSearchResult( + val key: String, + val title: String, + val keywords: String? = null, + ) + interface MobileNetworkSettingsSearchItem { - val key: String - - val title: String - - val keywords: String? - get() = null - - fun isAvailable(subId: Int): Boolean + fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? } fun createSearchIndexableData(): SearchIndexableData { @@ -71,13 +70,15 @@ class MobileNetworkSettingsSearchIndex( searchItem: MobileNetworkSettingsSearchItem, subInfos: List ): List = - subInfos - .filter { searchItem.isAvailable(it.subscriptionId) } - .map { subInfo -> searchIndexableRaw(context, searchItem, subInfo) } + subInfos.mapNotNull { subInfo -> + searchItem.getSearchResult(subInfo.subscriptionId)?.let { searchResult -> + searchIndexableRaw(context, searchResult, subInfo) + } + } private fun searchIndexableRaw( context: Context, - searchItem: MobileNetworkSettingsSearchItem, + searchResult: MobileNetworkSettingsSearchResult, subInfo: SubscriptionInfo, ): SearchIndexableRaw { val key = @@ -85,7 +86,7 @@ class MobileNetworkSettingsSearchIndex( .setFragment( SpaSearchLandingFragment.newBuilder() .setFragmentName(MobileNetworkSettings::class.java.name) - .setPreferenceKey(searchItem.key) + .setPreferenceKey(searchResult.key) .putArguments( Settings.EXTRA_SUB_ID, BundleValue.newBuilder().setIntValue(subInfo.subscriptionId).build())) @@ -94,8 +95,8 @@ class MobileNetworkSettingsSearchIndex( return createSearchIndexableRaw( context = context, spaSearchLandingKey = key, - itemTitle = searchItem.title, - keywords = searchItem.keywords, + itemTitle = searchResult.title, + keywords = searchResult.keywords, indexableClass = MobileNetworkSettings::class.java, pageTitle = "$simsTitle > ${subInfo.displayName}", ) diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt index 5c94e848270..0d8766e4df0 100644 --- a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreference @@ -79,12 +80,17 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( companion object { class NrAdvancedCallingSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem { - override val key = "nr_advanced_calling" - override val title: String = context.getString(R.string.nr_advanced_calling_title) - override val keywords: String = context.getString(R.string.keywords_nr_advanced_calling) - override fun isAvailable(subId: Int): Boolean = - VoNrRepository(context, subId).isVoNrAvailable() + fun isAvailable(subId: Int): Boolean = VoNrRepository(context, subId).isVoNrAvailable() + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = "nr_advanced_calling", + title = context.getString(R.string.nr_advanced_calling_title), + keywords = context.getString(R.string.keywords_nr_advanced_calling), + ) + } } } } diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt index 7633677dcb4..a5ac7d66640 100644 --- a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.fragment.app.FragmentManager import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel @@ -98,16 +99,21 @@ constructor( companion object { private const val DIALOG_TAG = "MobileDataDialog" - class RoamingSearchItem(context: Context) : MobileNetworkSettingsSearchItem { - override val key = "button_roaming_key" - override val title: String = context.getString(R.string.roaming) - + class RoamingSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem { private val carrierConfigRepository = CarrierConfigRepository(context) - override fun isAvailable(subId: Int): Boolean = + fun isAvailable(subId: Int): Boolean = SubscriptionManager.isValidSubscriptionId(subId) && !carrierConfigRepository.getBoolean( subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL) + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = "button_roaming_key", + title = context.getString(R.string.roaming), + ) + } } } } diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt index 7e8e58cceb5..e04763a88c1 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt @@ -27,6 +27,7 @@ import androidx.preference.Preference import androidx.preference.PreferenceScreen import com.android.settings.R import com.android.settings.core.BasePreferenceController +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.network.telephony.wificalling.WifiCallingRepository import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle @@ -132,12 +133,17 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( class WifiCallingSearchItem( private val context: Context, ) : MobileNetworkSettingsSearchItem { - override val key: String = "wifi_calling" - override val title: String = context.getString(R.string.wifi_calling_settings_title) - - override fun isAvailable(subId: Int): Boolean = runBlocking { + private fun isAvailable(subId: Int): Boolean = runBlocking { WifiCallingRepository(context, subId).wifiCallingReadyFlow().first() } + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = "wifi_calling", + title = context.getString(R.string.wifi_calling_settings_title), + ) + } } } } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt index 5e7e83c9f43..bf5120817c1 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt @@ -25,6 +25,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.Companion.isMobileNetworkSettingsSearchable +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.spa.SpaSearchLanding.BundleValue import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey @@ -62,10 +63,12 @@ class MobileNetworkSettingsSearchIndexTest { private val mobileNetworkSettingsSearchIndex = MobileNetworkSettingsSearchIndex { listOf( object : MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem { - override val key = KEY - override val title = TITLE - - override fun isAvailable(subId: Int) = subId == SUB_ID_1 + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? = + if (subId == SUB_ID_1) { + MobileNetworkSettingsSearchResult(key = KEY, title = TITLE) + } else { + null + } }) } From 25d5816f5fb8d1624cef5cd28c62eb46068bb2d7 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 8 Aug 2024 10:54:47 +0800 Subject: [PATCH 06/11] Dual SIM search for Preferred network type Bug: 358238959 Flag: EXEMPT bug fix Test: manual - search preferred Test: atest EnabledNetworkModePreferenceControllerHelperTest Change-Id: Icb86d88eec3bcaaaf1db67f7008173a90957cbbc --- res/xml/mobile_network_settings.xml | 4 + ...nabledNetworkModePreferenceController.java | 45 ++++----- ...edNetworkModePreferenceControllerHelper.kt | 65 +++++++++++++ .../MobileNetworkSettingsSearchIndex.kt | 1 + ...ferredNetworkModePreferenceController.java | 29 ++---- .../TelephonyBasePreferenceController.java | 28 ------ ...tworkModePreferenceControllerHelperTest.kt | 96 +++++++++++++++++++ ...edNetworkModePreferenceControllerTest.java | 78 --------------- ...edNetworkModePreferenceControllerTest.java | 40 -------- 9 files changed, 192 insertions(+), 194 deletions(-) create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 539c145e4e2..eb80ac84162 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -152,6 +152,7 @@ android:summary="@string/contact_discovery_opt_in_summary" settings:controller="com.android.settings.network.telephony.ContactDiscoveryPreferenceController"/> + + NetworkModePreferenceType.None + config.worldPhone -> NetworkModePreferenceType.PreferredNetworkMode + else -> NetworkModePreferenceType.EnabledNetworkMode + } +} + +class PreferredNetworkModeSearchItem(private val context: Context) : + MobileNetworkSettingsSearchItem { + private val title: String = context.getString(R.string.preferred_network_mode_title) + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? = + when (getNetworkModePreferenceType(context, subId)) { + NetworkModePreferenceType.PreferredNetworkMode -> + MobileNetworkSettingsSearchResult( + key = "preferred_network_mode_key", + title = title, + ) + + NetworkModePreferenceType.EnabledNetworkMode -> + MobileNetworkSettingsSearchResult( + key = "enabled_networks_key", + title = title, + ) + + else -> null + } +} diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index b702054643e..58661f0e4fb 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -116,6 +116,7 @@ class MobileNetworkSettingsSearchIndex( listOf( MmsMessageSearchItem(context), NrAdvancedCallingSearchItem(context), + PreferredNetworkModeSearchItem(context), RoamingSearchItem(context), WifiCallingSearchItem(context), ) diff --git a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java index bdfeace1be6..210cd879966 100644 --- a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.network.telephony; +import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.getNetworkModePreferenceType; + import android.content.Context; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; @@ -27,16 +29,18 @@ import androidx.preference.ListPreference; import androidx.preference.Preference; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.network.CarrierConfigCache; import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants; /** * Preference controller for "Preferred network mode" */ -public class PreferredNetworkModePreferenceController extends TelephonyBasePreferenceController +public class PreferredNetworkModePreferenceController extends BasePreferenceController implements ListPreference.OnPreferenceChangeListener { private static final String TAG = "PrefNetworkModeCtrl"; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private CarrierConfigCache mCarrierConfigCache; private TelephonyManager mTelephonyManager; private boolean mIsGlobalCdma; @@ -47,25 +51,10 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe } @Override - public int getAvailabilityStatus(int subId) { - final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId); - boolean visible; - if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - visible = false; - } else if (carrierConfig == null) { - visible = false; - } else if (carrierConfig.getBoolean( - CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) - || carrierConfig.getBoolean( - CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL)) { - visible = false; - } else if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) { - visible = true; - } else { - visible = false; - } - - return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + public int getAvailabilityStatus() { + return getNetworkModePreferenceType(mContext, mSubId) + == NetworkModePreferenceType.PreferredNetworkMode + ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override diff --git a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java index 3972f3900f3..ee1552083ea 100644 --- a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java +++ b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java @@ -17,9 +17,6 @@ package com.android.settings.network.telephony; import android.content.Context; -import android.content.res.Resources; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import com.android.settings.core.BasePreferenceController; @@ -59,29 +56,4 @@ public abstract class TelephonyBasePreferenceController extends BasePreferenceCo public void unsetAvailabilityStatus() { mSetSessionCount.getAndDecrement(); } - - /** - * Get carrier config based on specific subscription id. - * - * @param subId is the subscription id - * @return {@link PersistableBundle} of carrier config, or {@code null} when carrier config - * is not available. - */ - public PersistableBundle getCarrierConfigForSubId(int subId) { - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - return null; - } - final CarrierConfigManager carrierConfigMgr = - mContext.getSystemService(CarrierConfigManager.class); - return carrierConfigMgr.getConfigForSubId(subId); - } - - /** - * Returns the resources associated with Subscription. - * - * @return Resources associated with Subscription. - */ - public Resources getResourcesForSubId() { - return SubscriptionManager.getResourcesForSubId(mContext, mSubId); - } } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt new file mode 100644 index 00000000000..8edc90fa24c --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt @@ -0,0 +1,96 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.spy + +@RunWith(AndroidJUnit4::class) +class EnabledNetworkModePreferenceControllerHelperTest { + + private var context: Context = spy(ApplicationProvider.getApplicationContext()) {} + + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true) + } + + @Test + fun getNetworkModePreferenceType_hideCarrierNetworkSettings_returnNone() { + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, true) + + val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID) + + assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None) + } + + @Test + fun getNetworkModePreferenceType_hidePreferredNetworkType_returnNone() { + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, true) + + val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID) + + assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None) + } + + @Test + fun getNetworkModePreferenceType_carrierConfigNotReady_returnNone() { + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false) + + val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID) + + assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None) + } + + @Test + fun getNetworkModePreferenceType_isWorldPhone_returnPreferredNetworkMode() { + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true) + + val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID) + + assertThat(networkModePreferenceType) + .isEqualTo(NetworkModePreferenceType.PreferredNetworkMode) + } + + @Test + fun getNetworkModePreferenceType_notWorldPhone_returnEnabledNetworkMode() { + CarrierConfigRepository.setBooleanForTest( + SUB_ID, CarrierConfigManager.KEY_WORLD_PHONE_BOOL, false) + + val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID) + + assertThat(networkModePreferenceType) + .isEqualTo(NetworkModePreferenceType.EnabledNetworkMode) + } + + private companion object { + const val SUB_ID = 10 + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java index b3d095e5453..adc8dc0cdc4 100644 --- a/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java @@ -18,9 +18,6 @@ package com.android.settings.network.telephony; import static androidx.lifecycle.Lifecycle.Event.ON_START; -import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; -import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.android.settings.network.telephony.MobileNetworkUtils.getRafFromNetworkType; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.CDMA; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.EVDO; @@ -33,8 +30,6 @@ import static com.android.settings.network.telephony.TelephonyConstants.RadioAcc import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -123,79 +118,6 @@ public class EnabledNetworkModePreferenceControllerTest { mPreference.setKey(mController.getPreferenceKey()); } - @UiThreadTest - @Test - public void getAvailabilityStatus_hideCarrierNetworkSettings_returnUnavailable() { - mPersistableBundle.putBoolean( - CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, - true); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - - @UiThreadTest - @Test - public void getAvailabilityStatus_hidePreferredNetworkType_returnUnavailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, - true); - - when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); - when(mServiceState.getDataRegistrationState()).thenReturn( - ServiceState.STATE_OUT_OF_SERVICE); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - - when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); - when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE); - - when(mServiceState.getRoaming()).thenReturn(false); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - - when(mServiceState.getRoaming()).thenReturn(true); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - - @UiThreadTest - @Test - public void getAvailabilityStatus_carrierConfigNotReady_returnUnavailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - - @UiThreadTest - @Test - public void getAvailabilityStatus_notWorldPhone_returnAvailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, - false); - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, false); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @UiThreadTest - @Test - public void getAvailabilityStatus_callStateIsIdle_returnAvailable() { - mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA); - mController.getTelephonyCallback().onCallStateChanged(TelephonyManager.CALL_STATE_IDLE); - - mController.updateState(mPreference); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - assertTrue(mPreference.isEnabled()); - } - - @UiThreadTest - @Test - public void getAvailabilityStatus_duringCalling_returnAvailable() { - mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA); - mController.getTelephonyCallback().onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK); - - mController.updateState(mPreference); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); - assertFalse(mPreference.isEnabled()); - } - @UiThreadTest @Test public void updateState_LteWorldPhone_GlobalHasLte() { diff --git a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java index 9dbfdde3d3b..f22ad3bce8d 100644 --- a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java @@ -16,8 +16,6 @@ package com.android.settings.network.telephony; -import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.GSM; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.RAF_TD_SCDMA; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.WCDMA; @@ -32,7 +30,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -91,43 +88,6 @@ public class PreferredNetworkModePreferenceControllerTest { mPreference.setKey(mController.getPreferenceKey()); } - @Test - public void getAvailabilityStatus_hideCarrierNetworkSettings_returnUnavailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, - true); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_worldPhone_returnAvailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, - false); - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @Test - public void getAvailabilityStatus_hidePreferredNetworkType_returnUnavailable() { - mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, - true); - - when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); - when(mServiceState.getDataRegistrationState()).thenReturn( - ServiceState.STATE_OUT_OF_SERVICE); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - - when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); - when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE); - - when(mServiceState.getRoaming()).thenReturn(false); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - - when(mServiceState.getRoaming()).thenReturn(true); - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - @Test public void updateState_updateByNetworkMode() { // NETWORK_MODE_TDSCDMA_GSM_WCDMA = RAF_TD_SCDMA | GSM | WCDMA From a26baa009609a902212719269f0a5e1e76e4a921 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Thu, 8 Aug 2024 17:55:18 +0800 Subject: [PATCH 07/11] [Audiosharing] Fix stop sharing btn in notification When the broadcast is off but the notification mis-stay, click on "Stop sharing" should dismiss the notification. Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Bug: 305620450 Change-Id: Ic5b30f29b8acc06244c38eb357f63ed83985d74a --- .../audiosharing/AudioSharingReceiver.java | 12 +++++-- .../AudioSharingReceiverTest.java | 31 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java index b43a544e4d6..371613f461d 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java @@ -81,9 +81,15 @@ public class AudioSharingReceiver extends BroadcastReceiver { break; case ACTION_LE_AUDIO_SHARING_STOP: LocalBluetoothManager manager = Utils.getLocalBtManager(context); - AudioSharingUtils.stopBroadcasting(manager); - metricsFeatureProvider.action( - context, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION); + if (BluetoothUtils.isBroadcasting(manager)) { + AudioSharingUtils.stopBroadcasting(manager); + metricsFeatureProvider.action( + context, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION); + } else { + cancelSharingNotification(context); + metricsFeatureProvider.action( + context, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION); + } break; default: Log.w(TAG, "Received unexpected intent " + intent.getAction()); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java index deed229bbcb..db6eb8c72de 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java @@ -25,7 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -164,8 +164,7 @@ public class AudioSharingReceiverTest { AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); - verify(mNm, times(1)) - .notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class)); + verify(mNm).notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class)); verify(mFeatureFactory.metricsFeatureProvider) .action(mContext, SettingsEnums.ACTION_SHOW_AUDIO_SHARING_NOTIFICATION); } @@ -181,7 +180,7 @@ public class AudioSharingReceiverTest { AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); - verify(mNm, times(1)).cancel(R.drawable.ic_bt_le_audio_sharing); + verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing); verify(mFeatureFactory.metricsFeatureProvider) .action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION); } @@ -199,8 +198,10 @@ public class AudioSharingReceiverTest { } @Test - public void broadcastReceiver_receiveAudioSharingStopIntent_stopBroadcast() { + public void + broadcastReceiver_receiveAudioSharingStopIntent_notInBroadcast_cancelNotification() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + when(mBroadcast.isEnabled(null)).thenReturn(false); int broadcastId = 1; when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId); @@ -209,7 +210,25 @@ public class AudioSharingReceiverTest { AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); - verify(mBroadcast, times(1)).stopBroadcast(broadcastId); + verify(mBroadcast, never()).stopBroadcast(broadcastId); + verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing); + verify(mFeatureFactory.metricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION); + } + + @Test + public void broadcastReceiver_receiveAudioSharingStopIntent_inBroadcast_stopBroadcast() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + when(mBroadcast.isEnabled(null)).thenReturn(true); + int broadcastId = 1; + when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STOP); + intent.setPackage(mContext.getPackageName()); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mBroadcast).stopBroadcast(broadcastId); verify(mFeatureFactory.metricsFeatureProvider) .action(mContext, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION); } From 15c6533ef9de5b730fda33be3d27e8c7bbb14551 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Fri, 19 Jul 2024 19:16:11 +0800 Subject: [PATCH 08/11] Connected devices page did not show correct summary when member device connect Root Cause: CsipDeviceManager only refreshes UI when switching member device content. Solution: * CsipDeviceManager needs to call refresh() on main device when it's new member device added. * UI widget Settings/BluetoothDevice also need to monitor it's member device status to refresh UI. Bug: 344947362 Test: atest BluetoothDevicePreferenceTest Flag: EXEMPT bugfix Change-Id: I58f9e2fc209d4e87631784d0538b1647228f4c1a --- .../BluetoothDetailsProfilesController.java | 18 +- .../bluetooth/BluetoothDevicePreference.java | 50 ++++-- ...AudioBluetoothDetailsHeaderController.java | 22 +-- src/com/android/settings/bluetooth/Utils.java | 19 +- .../BluetoothDevicePreferenceTest.java | 169 ++++++++++++++---- 5 files changed, 204 insertions(+), 74 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index 2b746842ad6..0897a4379b5 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -89,7 +89,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll private LocalBluetoothManager mManager; private LocalBluetoothProfileManager mProfileManager; private CachedBluetoothDevice mCachedDevice; - private List mAllOfCachedDevices; + private Set mCachedDeviceGroup; private Map> mProfileDeviceMap = new HashMap>(); private boolean mIsLeContactSharingEnabled = false; @@ -105,7 +105,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mManager = manager; mProfileManager = mManager.getProfileManager(); mCachedDevice = device; - mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice); + mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); } @Override @@ -310,10 +310,10 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll private List getProfiles() { List result = new ArrayList<>(); mProfileDeviceMap.clear(); - if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) { + if (mCachedDeviceGroup == null || mCachedDeviceGroup.isEmpty()) { return result; } - for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) { + for (CachedBluetoothDevice cachedItem : mCachedDeviceGroup) { List tmpResult = cachedItem.getUiAccessibleProfiles(); for (LocalBluetoothProfile profile : tmpResult) { if (mProfileDeviceMap.containsKey(profile.toString())) { @@ -514,7 +514,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll @Override public void onPause() { - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.unregisterCallback(this); } mProfileManager.removeServiceListener(this); @@ -523,7 +523,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll @Override public void onResume() { updateLeAudioConfig(); - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.registerCallback(this); } mProfileManager.addServiceListener(this); @@ -545,11 +545,11 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll @Override public void onDeviceAttributesChanged() { - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.unregisterCallback(this); } - mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice); - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.registerCallback(this); } diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index ac0c63bc6de..209c900927e 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -46,6 +46,7 @@ import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.GearPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; @@ -55,6 +56,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** * BluetoothDevicePreference is the preference type used to display each remote @@ -76,7 +78,10 @@ public final class BluetoothDevicePreference extends GearPreference { } private final CachedBluetoothDevice mCachedDevice; + private Set mCachedDeviceGroup; + private final UserManager mUserManager; + private final LocalBluetoothManager mLocalBtManager; private Set mBluetoothDevices; @VisibleForTesting @@ -113,6 +118,21 @@ public final class BluetoothDevicePreference extends GearPreference { @Override public void onDeviceAttributesChanged() { onPreferenceAttributesChanged(); + Set newCachedDeviceGroup = new HashSet<>( + Utils.findAllCachedBluetoothDevicesByGroupId(mLocalBtManager, mCachedDevice)); + if (!mCachedDeviceGroup.equals(newCachedDeviceGroup)) { + for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) { + cachedBluetoothDevice.unregisterCallback(this); + } + unregisterMetadataChangedListener(); + + mCachedDeviceGroup = newCachedDeviceGroup; + + for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) { + cachedBluetoothDevice.registerCallback(getContext().getMainExecutor(), this); + } + registerMetadataChangedListener(); + } } } @@ -121,6 +141,7 @@ public final class BluetoothDevicePreference extends GearPreference { super(context, null); mResources = getContext().getResources(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mLocalBtManager = Utils.getLocalBluetoothManager(context); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mShowDevicesWithoutNames = showDeviceWithoutNames; @@ -131,6 +152,8 @@ public final class BluetoothDevicePreference extends GearPreference { } mCachedDevice = cachedDevice; + mCachedDeviceGroup = new HashSet<>( + Utils.findAllCachedBluetoothDevicesByGroupId(mLocalBtManager, mCachedDevice)); mCallback = new BluetoothDevicePreferenceCallback(); mId = sNextId.getAndIncrement(); mType = type; @@ -164,7 +187,9 @@ public final class BluetoothDevicePreference extends GearPreference { protected void onPrepareForRemoval() { super.onPrepareForRemoval(); if (!mIsCallbackRemoved) { - mCachedDevice.unregisterCallback(mCallback); + for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) { + cachedBluetoothDevice.unregisterCallback(mCallback); + } unregisterMetadataChangedListener(); mIsCallbackRemoved = true; } @@ -178,7 +203,9 @@ public final class BluetoothDevicePreference extends GearPreference { public void onAttached() { super.onAttached(); if (mIsCallbackRemoved) { - mCachedDevice.registerCallback(mCallback); + for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) { + cachedBluetoothDevice.registerCallback(getContext().getMainExecutor(), mCallback); + } registerMetadataChangedListener(); mIsCallbackRemoved = false; } @@ -189,7 +216,9 @@ public final class BluetoothDevicePreference extends GearPreference { public void onDetached() { super.onDetached(); if (!mIsCallbackRemoved) { - mCachedDevice.unregisterCallback(mCallback); + for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) { + cachedBluetoothDevice.unregisterCallback(mCallback); + } unregisterMetadataChangedListener(); mIsCallbackRemoved = true; } @@ -200,16 +229,11 @@ public final class BluetoothDevicePreference extends GearPreference { Log.d(TAG, "No mBluetoothAdapter"); return; } - if (mBluetoothDevices == null) { - mBluetoothDevices = new HashSet<>(); - } - mBluetoothDevices.clear(); - if (mCachedDevice.getDevice() != null) { - mBluetoothDevices.add(mCachedDevice.getDevice()); - } - for (CachedBluetoothDevice cbd : mCachedDevice.getMemberDevice()) { - mBluetoothDevices.add(cbd.getDevice()); - } + + mBluetoothDevices = mCachedDeviceGroup.stream() + .map(CachedBluetoothDevice::getDevice) + .collect(Collectors.toCollection(HashSet::new)); + if (mBluetoothDevices.isEmpty()) { Log.d(TAG, "No BT device to register."); return; diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java index 4be4d63d7c3..a5e9cde2d17 100644 --- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java @@ -47,7 +47,7 @@ import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.widget.LayoutPreference; -import java.util.List; +import java.util.Set; /** * This class adds a header with device name and status (connected/disconnected, etc.). @@ -90,7 +90,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr LayoutPreference mLayoutPreference; LocalBluetoothManager mManager; private CachedBluetoothDevice mCachedDevice; - private List mAllOfCachedDevices; + private Set mCachedDeviceGroup; @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper()); @VisibleForTesting @@ -128,7 +128,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr return; } mIsRegisterCallback = true; - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.registerCallback(this); } refresh(); @@ -139,7 +139,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr if (!mIsRegisterCallback) { return; } - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.unregisterCallback(this); } @@ -155,7 +155,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr mCachedDevice = cachedBluetoothDevice; mManager = bluetoothManager; mProfileManager = bluetoothManager.getProfileManager(); - mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice); + mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); } @VisibleForTesting @@ -230,7 +230,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr // Init the battery layouts. hideAllOfBatteryLayouts(); LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile(); - if (mAllOfCachedDevices.isEmpty()) { + if (mCachedDeviceGroup.isEmpty()) { Log.e(TAG, "There is no LeAudioProfile."); return; } @@ -244,7 +244,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr return; } - for (CachedBluetoothDevice cachedDevice : mAllOfCachedDevices) { + for (CachedBluetoothDevice cachedDevice : mCachedDeviceGroup) { int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice()); Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress() + ", deviceId:" + deviceId); @@ -300,15 +300,15 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr @Override public void onDeviceAttributesChanged() { - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.unregisterCallback(this); } - mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice); - for (CachedBluetoothDevice item : mAllOfCachedDevices) { + mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); + for (CachedBluetoothDevice item : mCachedDeviceGroup) { item.registerCallback(this); } - if (!mAllOfCachedDevices.isEmpty()) { + if (!mCachedDeviceGroup.isEmpty()) { refresh(); } } diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java index f6288b2c85c..b1d9de7bd3d 100644 --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -48,8 +48,9 @@ import com.android.settingslib.utils.ThreadUtils; import com.google.common.base.Supplier; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; @@ -239,12 +240,12 @@ public final class Utils { * @param cachedBluetoothDevice The main cachedBluetoothDevice. * @return all cachedBluetoothDevices with the same groupId. */ - public static List getAllOfCachedBluetoothDevices( + public static Set findAllCachedBluetoothDevicesByGroupId( LocalBluetoothManager localBtMgr, CachedBluetoothDevice cachedBluetoothDevice) { - List cachedBluetoothDevices = new ArrayList<>(); + Set cachedBluetoothDevices = new HashSet<>(); if (cachedBluetoothDevice == null) { - Log.e(TAG, "getAllOfCachedBluetoothDevices: no cachedBluetoothDevice"); + Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: no cachedBluetoothDevice"); return cachedBluetoothDevices; } int deviceGroupId = cachedBluetoothDevice.getGroupId(); @@ -254,7 +255,7 @@ public final class Utils { } if (localBtMgr == null) { - Log.e(TAG, "getAllOfCachedBluetoothDevices: no LocalBluetoothManager"); + Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: no LocalBluetoothManager"); return cachedBluetoothDevices; } CachedBluetoothDevice mainDevice = @@ -262,16 +263,14 @@ public final class Utils { .filter(cachedDevice -> cachedDevice.getGroupId() == deviceGroupId) .findFirst().orElse(null); if (mainDevice == null) { - Log.e(TAG, "getAllOfCachedBluetoothDevices: groupId = " + deviceGroupId + Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: groupId = " + deviceGroupId + ", no main device."); return cachedBluetoothDevices; } cachedBluetoothDevice = mainDevice; cachedBluetoothDevices.add(cachedBluetoothDevice); - for (CachedBluetoothDevice member : cachedBluetoothDevice.getMemberDevice()) { - cachedBluetoothDevices.add(member); - } - Log.d(TAG, "getAllOfCachedBluetoothDevices: groupId = " + deviceGroupId + cachedBluetoothDevices.addAll(cachedBluetoothDevice.getMemberDevice()); + Log.d(TAG, "findAllCachedBluetoothDevicesByGroupId: groupId = " + deviceGroupId + " , cachedBluetoothDevice = " + cachedBluetoothDevice + " , deviceList = " + cachedBluetoothDevices); return cachedBluetoothDevices; diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java index 03113421d3f..ba90ccf63d0 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java @@ -18,10 +18,10 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,22 +32,31 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.os.UserManager; import android.util.Pair; -import android.view.ContextThemeWrapper; + +import androidx.test.core.app.ApplicationProvider; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; @@ -57,18 +66,21 @@ import java.util.Comparator; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowAlertDialogCompat.class}) +@Config(shadows = {ShadowAlertDialogCompat.class, + com.android.settings.testutils.shadow.ShadowBluetoothUtils.class}) public class BluetoothDevicePreferenceTest { private static final boolean SHOW_DEVICES_WITHOUT_NAMES = true; - private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; - private static final String MAC_ADDRESS_2 = "05:52:C7:0B:D8:3C"; - private static final String MAC_ADDRESS_3 = "06:52:C7:0B:D8:3C"; - private static final String MAC_ADDRESS_4 = "07:52:C7:0B:D8:3C"; + private static final String TEST_MAC_ADDRESS = "04:52:C7:0B:D8:3C"; + private static final String TEST_MAC_ADDRESS_1 = "05:52:C7:0B:D8:3C"; + private static final String TEST_MAC_ADDRESS_2 = "06:52:C7:0B:D8:3C"; + private static final String TEST_MAC_ADDRESS_3 = "07:52:C7:0B:D8:3C"; private static final Comparator COMPARATOR = Comparator.naturalOrder(); private static final String FAKE_DESCRIPTION = "fake_description"; + private static final int TEST_DEVICE_GROUP_ID = 1; - private Context mContext; + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); @Mock private CachedBluetoothDevice mCachedBluetoothDevice; @Mock @@ -89,35 +101,37 @@ public class BluetoothDevicePreferenceTest { private Drawable mDrawable; @Mock private BluetoothAdapter mBluetoothAdapter; + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + private Context mContext = ApplicationProvider.getApplicationContext(); private FakeFeatureFactory mFakeFeatureFactory; private MetricsFeatureProvider mMetricsFeatureProvider; + private BluetoothDevicePreference mPreference; private List mPreferenceList = new ArrayList<>(); @Before public void setUp() { - MockitoAnnotations.initMocks(this); - Context context = spy(RuntimeEnvironment.application.getApplicationContext()); - mContext = new ContextThemeWrapper(context, R.style.Theme_Settings); + mContext.setTheme(R.style.Theme_Settings); mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider(); - when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS); - when(mCachedBluetoothDevice.getDrawableWithDescription()) - .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); - when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); - when(mCachedDevice1.getAddress()).thenReturn(MAC_ADDRESS_2); - when(mCachedDevice1.getDrawableWithDescription()) - .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); - when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1); - when(mCachedDevice2.getAddress()).thenReturn(MAC_ADDRESS_3); - when(mCachedDevice2.getDrawableWithDescription()) - .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); - when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2); - when(mCachedDevice3.getAddress()).thenReturn(MAC_ADDRESS_4); - when(mCachedDevice3.getDrawableWithDescription()) - .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); - when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3); + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; + mLocalBluetoothManager = Utils.getLocalBtManager(mContext); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); + prepareCachedBluetoothDevice(mCachedBluetoothDevice, TEST_MAC_ADDRESS, + new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice); + prepareCachedBluetoothDevice(mCachedDevice1, TEST_MAC_ADDRESS_1, + new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice1); + prepareCachedBluetoothDevice(mCachedDevice2, TEST_MAC_ADDRESS_2, + new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice2); + prepareCachedBluetoothDevice(mCachedDevice3, TEST_MAC_ADDRESS_3, + new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice3); + when(mDeviceManager.getCachedDevicesCopy()).thenReturn( + ImmutableList.of(mCachedBluetoothDevice)); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT); mPreference.mBluetoothAdapter = mBluetoothAdapter; @@ -301,7 +315,8 @@ public class BluetoothDevicePreferenceTest { // callback is not removed. mPreference.onAttached(); - verify(mCachedBluetoothDevice, times(1)).registerCallback(any()); + verify(mCachedBluetoothDevice, times(1)).registerCallback(eq(mContext.getMainExecutor()), + any()); verify(mBluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any()); } @@ -313,7 +328,99 @@ public class BluetoothDevicePreferenceTest { mPreference.onAttached(); verify(mCachedBluetoothDevice, times(1)).unregisterCallback(any()); - verify(mCachedBluetoothDevice, times(2)).registerCallback(any()); + verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()), + any()); verify(mBluetoothAdapter, times(2)).addOnMetadataChangedListener(any(), any(), any()); } + + @Test + public void onDeviceAttributesChanged_updatePreference() { + when(mCachedBluetoothDevice.getName()).thenReturn("Name"); + mPreference.onAttached(); + final String updatedName = "updatedName"; + when(mCachedBluetoothDevice.getName()).thenReturn(updatedName); + + getCachedBluetoothDeviceCallback().onDeviceAttributesChanged(); + + assertThat(mPreference.getTitle().toString()).isEqualTo(updatedName); + } + + @Test + public void onAttached_memberDevicesAdded_registerAllCallback() { + when(mCachedBluetoothDevice.getMemberDevice()).thenReturn( + ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3)); + when(mDeviceManager.getCachedDevicesCopy()).thenReturn( + ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2, + mCachedDevice3)); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, + SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT); + + mPreference.onAttached(); + + verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()), any()); + verify(mCachedDevice1).registerCallback(eq(mContext.getMainExecutor()), any()); + verify(mCachedDevice2).registerCallback(eq(mContext.getMainExecutor()), any()); + verify(mCachedDevice3).registerCallback(eq(mContext.getMainExecutor()), any()); + } + + @Test + public void onDetached_memberDevicesAdded_unregisterAllCallback() { + when(mCachedBluetoothDevice.getMemberDevice()).thenReturn( + ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3)); + when(mDeviceManager.getCachedDevicesCopy()).thenReturn( + ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2, + mCachedDevice3)); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, + SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT); + + mPreference.onAttached(); + mPreference.onDetached(); + + verify(mCachedBluetoothDevice).unregisterCallback(any()); + verify(mCachedDevice1).unregisterCallback(any()); + verify(mCachedDevice2).unregisterCallback(any()); + verify(mCachedDevice3).unregisterCallback(any()); + } + + @Test + public void onDeviceAttributesChanged_memberDevicesChanged_registerOnlyExistDeviceCallback() { + when(mCachedBluetoothDevice.getMemberDevice()).thenReturn( + ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3)); + when(mDeviceManager.getCachedDevicesCopy()).thenReturn( + ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2, + mCachedDevice3)); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, + SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT); + mPreference.onAttached(); + when(mCachedBluetoothDevice.getMemberDevice()).thenReturn( + ImmutableSet.of(mCachedDevice1, mCachedDevice2)); + when(mDeviceManager.getCachedDevicesCopy()).thenReturn( + ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2)); + + getCachedBluetoothDeviceCallback().onDeviceAttributesChanged(); + + verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()), + any()); + verify(mCachedDevice1, times(2)).registerCallback(eq(mContext.getMainExecutor()), any()); + verify(mCachedDevice2, times(2)).registerCallback(eq(mContext.getMainExecutor()), any()); + verify(mCachedDevice3, times(1)).registerCallback(eq(mContext.getMainExecutor()), any()); + } + + private void prepareCachedBluetoothDevice(CachedBluetoothDevice cachedDevice, String address, + Pair drawableWithDescription, int groupId, + BluetoothDevice bluetoothDevice) { + when(cachedDevice.getAddress()).thenReturn(address); + when(cachedDevice.getDrawableWithDescription()).thenReturn(drawableWithDescription); + when(cachedDevice.getGroupId()).thenReturn(groupId); + when(cachedDevice.getDevice()).thenReturn(bluetoothDevice); + } + + private CachedBluetoothDevice.Callback getCachedBluetoothDeviceCallback() { + ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( + CachedBluetoothDevice.Callback.class); + verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()), + callbackCaptor.capture()); + + return callbackCaptor.getValue(); + } } From 7b0e5560d1920ee41e1f5ec708da80e1f7232831 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Wed, 7 Aug 2024 05:09:35 +0800 Subject: [PATCH 09/11] Fix WifiQrCode scheme inconsistency issue - New WifiQrCode design has been moved to WiFi Framework module - URI scheme changed from string "DPP" to integer 2 Bug: 356971926 Flag: EXEMPT bugfix Test: manual test by 15-dpp-fail.apk Change-Id: I00834ccb32f3ced8345213eef55b5aaa77006f7a --- src/com/android/settings/wifi/dpp/WifiQrCode.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/wifi/dpp/WifiQrCode.java b/src/com/android/settings/wifi/dpp/WifiQrCode.java index 9b93480fd68..78e06253882 100644 --- a/src/com/android/settings/wifi/dpp/WifiQrCode.java +++ b/src/com/android/settings/wifi/dpp/WifiQrCode.java @@ -46,6 +46,7 @@ import android.util.Log; * */ public class WifiQrCode { + private static final String TAG = "WifiQrCode"; static final String SCHEME_DPP = "DPP"; static final String SCHEME_ZXING_WIFI_NETWORK_CONFIG = "WIFI"; static final String PREFIX_DPP = "DPP:"; @@ -119,13 +120,13 @@ public class WifiQrCode { try { wifiQrCode = new WifiQrCode(qrCode); } catch(IllegalArgumentException e) { + Log.e(TAG, "Failed to create WifiQrCode!", e); return null; } - - if (SCHEME_DPP.equals(wifiQrCode.getScheme())) { - return wifiQrCode; + if (wifiQrCode.getScheme() != UriParserResults.URI_SCHEME_DPP) { + Log.e(TAG, "wifiQrCode scheme is not DPP!"); + return null; } - - return null; + return wifiQrCode; } } From 73803595208605b9cc7a5162824cfb413cbac348 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 9 Aug 2024 05:32:39 +0000 Subject: [PATCH 10/11] Reduce flaky of AppNotificationPreferenceTest Bug: 355413226 Flag: EXEMPT test only Test: atest AppNotificationPreferenceTest Change-Id: I1dfe4c5ae764941acb78e8fad52518963d1dad74 --- .../appinfo/AppNotificationPreferenceTest.kt | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt index 37f3a119d0a..7a0ec8c7f13 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onRoot @@ -34,36 +34,35 @@ import com.android.settings.applications.appinfo.AppInfoDashboardFragment import com.android.settings.notification.app.AppNotificationSettings import com.android.settings.spa.notification.IAppNotificationRepository import com.android.settingslib.spa.testutils.delay +import com.android.settingslib.spa.testutils.waitUntilExists import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.MockitoSession -import org.mockito.Spy import org.mockito.quality.Strictness @RunWith(AndroidJUnit4::class) class AppNotificationPreferenceTest { - @get:Rule - val composeTestRule = createComposeRule() + @get:Rule val composeTestRule = createComposeRule() private lateinit var mockSession: MockitoSession - @Spy private val context: Context = ApplicationProvider.getApplicationContext() - private val repository = object : IAppNotificationRepository { - override fun getNotificationSummary(app: ApplicationInfo) = SUMMARY - } + private val repository = + object : IAppNotificationRepository { + override fun getNotificationSummary(app: ApplicationInfo) = SUMMARY + } @Before fun setUp() { - mockSession = ExtendedMockito.mockitoSession() - .initMocks(this) - .mockStatic(AppInfoDashboardFragment::class.java) - .strictness(Strictness.LENIENT) - .startMocking() + mockSession = + ExtendedMockito.mockitoSession() + .mockStatic(AppInfoDashboardFragment::class.java) + .strictness(Strictness.LENIENT) + .startMocking() } @After @@ -75,25 +74,26 @@ class AppNotificationPreferenceTest { fun title_displayed() { setContent(APP) - composeTestRule.onNodeWithText(context.getString(R.string.notifications_label)) - .assertIsDisplayed() + composeTestRule.waitUntilExists(hasText(context.getString(R.string.notifications_label))) } @Test fun summary_displayed() { setContent(APP) - composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed() + composeTestRule.waitUntilExists(hasText(SUMMARY)) } @Test fun whenNotInstalled_disable() { - setContent(ApplicationInfo().apply { - packageName = PACKAGE_NAME - uid = UID - }) + setContent( + ApplicationInfo().apply { + packageName = PACKAGE_NAME + uid = UID + }) - composeTestRule.onNodeWithText(context.getString(R.string.notifications_label)) + composeTestRule + .onNodeWithText(context.getString(R.string.notifications_label)) .assertIsNotEnabled() } @@ -125,11 +125,12 @@ class AppNotificationPreferenceTest { private companion object { const val PACKAGE_NAME = "package.name" const val UID = 123 - val APP = ApplicationInfo().apply { - packageName = PACKAGE_NAME - uid = UID - flags = ApplicationInfo.FLAG_INSTALLED - } + val APP = + ApplicationInfo().apply { + packageName = PACKAGE_NAME + uid = UID + flags = ApplicationInfo.FLAG_INSTALLED + } const val SUMMARY = "Summary" } -} \ No newline at end of file +} From 77ded32a4902f48d63cdc7be92b20b9cae34740d Mon Sep 17 00:00:00 2001 From: Wesley Wang Date: Fri, 9 Aug 2024 10:04:11 +0000 Subject: [PATCH 11/11] Fix NPE in DynamicDenylistManager Bug: 357280604 Change-Id: Ifcfe5cfea67f3ef692a865ef43db8e22de3cd68d Test: atest DynamicDenylistManagerTest Flag: EXEMPT for bug fix --- .../datasaver/DynamicDenylistManager.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java index b77d5eb6ddf..ecb2a48fe89 100644 --- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java +++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java @@ -78,6 +78,11 @@ public class DynamicDenylistManager { return; } + if (mNetworkPolicyManager == null) { + Log.w(TAG, "syncPolicyIfNeeded: invalid mNetworkPolicyManager"); + return; + } + final SharedPreferences.Editor editor = getManualDenylistPref().edit(); final int[] existedUids = mNetworkPolicyManager .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND); @@ -91,6 +96,11 @@ public class DynamicDenylistManager { /** Set policy flags for specific UID. */ public void setUidPolicyLocked(int uid, int policy) { + if (mNetworkPolicyManager == null) { + Log.w(TAG, "setUidPolicyLocked: invalid mNetworkPolicyManager"); + return; + } + Log.i(TAG, "setUidPolicyLocked: uid=" + uid + " policy=" + policy); synchronized (mLock) { mNetworkPolicyManager.setUidPolicy(uid, policy); @@ -100,7 +110,7 @@ public class DynamicDenylistManager { /** Suggest a list of package to set as POLICY_REJECT. */ public void setDenylist(Set denylistTargetUids) { - if (denylistTargetUids == null) { + if (denylistTargetUids == null || mNetworkPolicyManager == null) { return; } final Set manualDenylistUids = getDenylistAllUids(getManualDenylistPref()); @@ -164,6 +174,12 @@ public class DynamicDenylistManager { Log.w(TAG, "resetDenylistIfNeeded: invalid conditions"); return; } + + if (mNetworkPolicyManager == null) { + Log.w(TAG, "setUidPolicyLocked: invalid mNetworkPolicyManager"); + return; + } + synchronized (mLock) { final int[] uids = mNetworkPolicyManager .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);