diff --git a/res/values/strings.xml b/res/values/strings.xml index bd4b12b8c79..5aae3eb6729 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8847,6 +8847,13 @@ Notification bundles + + Notification summaries + On + Off + Use notification summaries + Automatically summarize conversation notifications from any app + Live notifications @@ -9269,7 +9276,6 @@ Use notification bundling Notifications with similar themes will be silenced and grouped together for a quieter experience. Bundling will override an app\'s own notification settings. - VR helper services diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml index e5fddc4e58e..a46769a6032 100644 --- a/res/xml/configure_notification_settings.xml +++ b/res/xml/configure_notification_settings.xml @@ -25,7 +25,7 @@ @@ -35,7 +35,7 @@ + + diff --git a/res/xml/mouse_settings.xml b/res/xml/mouse_settings.xml index 98a429fbe90..35279ec35ed 100644 --- a/res/xml/mouse_settings.xml +++ b/res/xml/mouse_settings.xml @@ -49,7 +49,7 @@ android:title="@string/trackpad_pointer_speed" android:order="40" android:selectable="false" - settings:controller="com.android.settings.inputmethod.TrackpadPointerSpeedPreferenceController"/> + settings:controller="com.android.settings.inputmethod.TouchpadPointerSpeedPreferenceController"/> + + + + + + + + + + diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java index 74903c03f91..9e71f7a4020 100644 --- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java +++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java @@ -44,11 +44,16 @@ public class ZenAccessDetails extends AppInfoWithHeader implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.zen_access_permission_details); + getSettingsLifecycle().addObserver( + new ZenAccessSettingObserverMixin(getContext(), this /* listener */)); + } + + @Override + public void onResume() { + super.onResume(); requireActivity().setTitle(Flags.modesApi() && Flags.modesUi() ? R.string.manage_zen_modes_access_title : R.string.manage_zen_access_title); - getSettingsLifecycle().addObserver( - new ZenAccessSettingObserverMixin(getContext(), this /* listener */)); } @Override diff --git a/src/com/android/settings/biometrics/ParentalControlsUtils.java b/src/com/android/settings/biometrics/ParentalControlsUtils.java index 8dd01f0ea57..04ab1b17596 100644 --- a/src/com/android/settings/biometrics/ParentalControlsUtils.java +++ b/src/com/android/settings/biometrics/ParentalControlsUtils.java @@ -17,6 +17,7 @@ package com.android.settings.biometrics; import android.app.admin.DevicePolicyManager; +import android.app.supervision.SupervisionManager; import android.content.ComponentName; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; @@ -44,6 +45,7 @@ public class ParentalControlsUtils { * {@link android.hardware.biometrics.ParentalControlsUtilsInternal#getTestComponentName} * @return non-null EnforcedAdmin if parental consent is required */ + @Nullable public static RestrictedLockUtils.EnforcedAdmin parentConsentRequired(@NonNull Context context, @BiometricAuthenticator.Modality int modality) { @@ -58,7 +60,11 @@ public class ParentalControlsUtils { } final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); - return parentConsentRequiredInternal(dpm, modality, userHandle); + final SupervisionManager sm = + android.app.supervision.flags.Flags.deprecateDpmSupervisionApis() + ? context.getSystemService(SupervisionManager.class) + : null; + return parentConsentRequiredInternal(dpm, sm, modality, userHandle); } /** @@ -68,16 +74,23 @@ public class ParentalControlsUtils { @Nullable @VisibleForTesting static RestrictedLockUtils.EnforcedAdmin parentConsentRequiredInternal( - @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality, + @NonNull DevicePolicyManager dpm, + @Nullable SupervisionManager sm, + @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) { - if (ParentalControlsUtilsInternal.parentConsentRequired(dpm, modality, - userHandle)) { + if (!ParentalControlsUtilsInternal.parentConsentRequired( + dpm, sm, modality, userHandle)) { + return null; + } + if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) { + // Supervision doesn't necessarily have have an admin component. + return new RestrictedLockUtils.EnforcedAdmin( + /* component= */ null, UserManager.DISALLOW_BIOMETRIC, userHandle); + } else { final ComponentName cn = ParentalControlsUtilsInternal.getSupervisionComponentName(dpm, userHandle); return new RestrictedLockUtils.EnforcedAdmin(cn, UserManager.DISALLOW_BIOMETRIC, userHandle); - } else { - return null; } } } diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index 5b1dbde910a..d7a747ce004 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -687,6 +687,37 @@ public class NotificationBackend { } } + public boolean isNotificationSummarizationSupported() { + try { + return !sINM.getUnsupportedAdjustmentTypes().contains(Adjustment.KEY_SUMMARIZATION); + } catch (Exception e) { + Log.w(TAG, "Error calling NoMan", e); + } + return false; + } + + public boolean isNotificationSummarizationEnabled(Context context) { + try { + return sINM.getAllowedAssistantAdjustments(context.getPackageName()) + .contains(Adjustment.KEY_SUMMARIZATION); + } catch (Exception e) { + Log.w(TAG, "Error calling NoMan", e); + } + return false; + } + + public void setNotificationSummarizationEnabled(boolean enabled) { + try { + if (enabled) { + sINM.allowAssistantAdjustment(Adjustment.KEY_SUMMARIZATION); + } else { + sINM.disallowAssistantAdjustment(Adjustment.KEY_SUMMARIZATION); + } + } catch (Exception e) { + Log.w(TAG, "Error calling NoMan", e); + } + } + public boolean isBundleTypeApproved(@Adjustment.Types int type) { try { int[] approved = sINM.getAllowedAdjustmentKeyTypes(); diff --git a/src/com/android/settings/notification/SummarizationGlobalPreferenceController.java b/src/com/android/settings/notification/SummarizationGlobalPreferenceController.java new file mode 100644 index 00000000000..0d66f37a91b --- /dev/null +++ b/src/com/android/settings/notification/SummarizationGlobalPreferenceController.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2025 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.notification; + +import android.app.Flags; +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.android.settings.widget.SettingsMainSwitchPreferenceController; + +public class SummarizationGlobalPreferenceController extends + SettingsMainSwitchPreferenceController { + + NotificationBackend mBackend; + + public SummarizationGlobalPreferenceController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + mBackend = new NotificationBackend(); + } + + @Override + public int getAvailabilityStatus() { + if ((Flags.nmSummarization() || Flags.nmSummarizationUi()) + && mBackend.isNotificationSummarizationSupported()) { + return AVAILABLE; + } + return CONDITIONALLY_UNAVAILABLE; + } + + @Override + public boolean isChecked() { + return mBackend.isNotificationSummarizationEnabled(mContext); + } + + @Override + public boolean setChecked(boolean isChecked) { + mBackend.setNotificationSummarizationEnabled(isChecked); + return true; + } + + @Override + public int getSliceHighlightMenuRes() { + // not needed since it's not sliceable + return NO_RES; + } +} diff --git a/src/com/android/settings/notification/SummarizationPreferenceController.java b/src/com/android/settings/notification/SummarizationPreferenceController.java new file mode 100644 index 00000000000..8857944c794 --- /dev/null +++ b/src/com/android/settings/notification/SummarizationPreferenceController.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 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.notification; + +import android.app.Flags; +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; + +/** + * Controller for the summarized notifications settings page. + */ +public class SummarizationPreferenceController extends BasePreferenceController { + + NotificationBackend mBackend; + + public SummarizationPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mBackend = new NotificationBackend(); + } + + @Override + public int getAvailabilityStatus() { + return (Flags.nmSummarization() || Flags.nmSummarizationUi()) + && mBackend.isNotificationSummarizationSupported() + ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public CharSequence getSummary() { + return mBackend.isNotificationSummarizationEnabled(mContext) + ? mContext.getString(R.string.notification_summarization_on) + : mContext.getString(R.string.notification_summarization_off); + } +} diff --git a/src/com/android/settings/notification/SummarizationPreferenceFragment.java b/src/com/android/settings/notification/SummarizationPreferenceFragment.java new file mode 100644 index 00000000000..1c8f5e3059d --- /dev/null +++ b/src/com/android/settings/notification/SummarizationPreferenceFragment.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 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.notification; + +import android.app.Flags; +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** + * Fragment for summarized notifications. + */ +@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) +public class SummarizationPreferenceFragment extends DashboardFragment { + + @Override + public int getMetricsCategory() { + return SettingsEnums.SUMMARIZED_NOTIFICATIONS; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.summarization_notifications_settings; + } + @Override + protected String getLogTag() { + return "SummarizationPreferenceFragment"; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.summarization_notifications_settings) { + + @Override + protected boolean isPageSearchEnabled(Context context) { + return Flags.notificationClassificationUi(); + } + }; +} diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java index 4b598db0ae7..ebf91bf0284 100644 --- a/src/com/android/settings/notification/zen/ZenAccessSettings.java +++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java @@ -69,9 +69,6 @@ public class ZenAccessSettings extends EmptyTextSettings implements mContext = getActivity(); mPkgMan = mContext.getPackageManager(); mNoMan = mContext.getSystemService(NotificationManager.class); - requireActivity().setTitle(Flags.modesApi() && Flags.modesUi() - ? R.string.manage_zen_modes_access_title - : R.string.manage_zen_access_title); getSettingsLifecycle().addObserver( new ZenAccessSettingObserverMixin(getContext(), this /* listener */)); } @@ -92,6 +89,9 @@ public class ZenAccessSettings extends EmptyTextSettings implements @Override public void onResume() { super.onResume(); + requireActivity().setTitle(Flags.modesApi() && Flags.modesUi() + ? R.string.manage_zen_modes_access_title + : R.string.manage_zen_access_title); reloadList(); } diff --git a/tests/robotests/src/com/android/settings/notification/SummarizationGlobalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SummarizationGlobalPreferenceControllerTest.java new file mode 100644 index 00000000000..b0bf5db6cb8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/SummarizationGlobalPreferenceControllerTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2025 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.notification; + +import static android.service.notification.Adjustment.KEY_IMPORTANCE; +import static android.service.notification.Adjustment.KEY_SUMMARIZATION; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Flags; +import android.app.INotificationManager; +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class SummarizationGlobalPreferenceControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private static final String PREFERENCE_KEY = "preference_key"; + + private Context mContext; + SummarizationGlobalPreferenceController mController; + @Mock + INotificationManager mInm; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mSetFlagsRule.enableFlags(Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI); + mController = new SummarizationGlobalPreferenceController(mContext, PREFERENCE_KEY); + mController.mBackend.setNm(mInm); + } + + @Test + public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception { + when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_SUMMARIZATION)); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION); + mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION_UI); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isChecked() throws Exception { + when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_SUMMARIZATION)); + assertThat(mController.isChecked()).isTrue(); + + when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_IMPORTANCE)); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void setChecked() throws Exception { + mController.setChecked(false); + verify(mInm).disallowAssistantAdjustment(KEY_SUMMARIZATION); + + mController.setChecked(true); + verify(mInm).allowAssistantAdjustment(KEY_SUMMARIZATION); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/SummarizationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SummarizationPreferenceControllerTest.java new file mode 100644 index 00000000000..ed93290696e --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/SummarizationPreferenceControllerTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 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.notification; + +import static android.service.notification.Adjustment.KEY_IMPORTANCE; +import static android.service.notification.Adjustment.KEY_SUMMARIZATION; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.app.Flags; +import android.app.INotificationManager; +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class SummarizationPreferenceControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private static final String PREFERENCE_KEY = "preference_key"; + + private Context mContext; + SummarizationPreferenceController mController; + @Mock + INotificationManager mInm; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mSetFlagsRule.enableFlags(Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI); + mController = new SummarizationPreferenceController(mContext, PREFERENCE_KEY); + mController.mBackend.setNm(mInm); + } + + @Test + public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception { + when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_SUMMARIZATION)); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION); + mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION_UI); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void getSummary() throws Exception { + when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_SUMMARIZATION)); + assertThat(mController.getSummary()).isEqualTo("On"); + + when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_IMPORTANCE)); + assertThat(mController.getSummary()).isEqualTo("Off"); + } +} diff --git a/tests/unit/src/com/android/settings/biometrics/ParentalControlsUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/ParentalControlsUtilsTest.java index 5128af0d410..2b6184b9153 100644 --- a/tests/unit/src/com/android/settings/biometrics/ParentalControlsUtilsTest.java +++ b/tests/unit/src/com/android/settings/biometrics/ParentalControlsUtilsTest.java @@ -16,26 +16,32 @@ package com.android.settings.biometrics; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS; + import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; +import android.app.supervision.SupervisionManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS; - import android.hardware.biometrics.BiometricAuthenticator; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -43,6 +49,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settingslib.RestrictedLockUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -50,11 +57,12 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class ParentalControlsUtilsTest { + @Rule public final CheckFlagsRule checkFlags = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Mock private Context mContext; + @Mock private DevicePolicyManager mDpm; + @Mock private SupervisionManager mSm; - @Mock - private Context mContext; - @Mock - private DevicePolicyManager mDpm; private ComponentName mSupervisionComponentName = new ComponentName("pkg", "cls"); @Before @@ -76,11 +84,28 @@ public class ParentalControlsUtilsTest { when(mDpm.getKeyguardDisabledFeatures(eq(supervisionComponentName))) .thenReturn(keyguardDisabledFlags); - return ParentalControlsUtils.parentConsentRequiredInternal(mDpm, modality, - new UserHandle(UserHandle.myUserId())); + return ParentalControlsUtils.parentConsentRequiredInternal( + mDpm, mSm, modality, new UserHandle(UserHandle.myUserId())); + } + + /** + * Helper that sets the appropriate mocks and testing behavior before returning the actual + * EnforcedAdmin from ParentalControlsUtils. + */ + @Nullable + private RestrictedLockUtils.EnforcedAdmin getEnforcedAdminForSupervision( + boolean supervisionEnabled, + @BiometricAuthenticator.Modality int modality, + int keyguardDisabledFlags) { + when(mSm.isSupervisionEnabledForUser(anyInt())).thenReturn(supervisionEnabled); + when(mDpm.getKeyguardDisabledFeatures(eq(null))).thenReturn(keyguardDisabledFlags); + + return ParentalControlsUtils.parentConsentRequiredInternal( + mDpm, mSm, modality, new UserHandle(UserHandle.myUserId())); } @Test + @RequiresFlagsDisabled(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS) public void testEnforcedAdmin_whenDpmDisablesBiometricsAndSupervisionComponentExists() { int[][] tests = { {TYPE_FINGERPRINT, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT}, @@ -99,6 +124,28 @@ public class ParentalControlsUtilsTest { } @Test + @RequiresFlagsEnabled(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS) + public void testEnforcedAdmin_whenDpmDisablesBiometricsAndSupervisionIsEnabled() { + int[][] tests = { + {TYPE_FINGERPRINT, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT}, + {TYPE_FACE, DevicePolicyManager.KEYGUARD_DISABLE_FACE}, + {TYPE_IRIS, DevicePolicyManager.KEYGUARD_DISABLE_IRIS}, + }; + + for (int i = 0; i < tests.length; i++) { + RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForSupervision( + /* supervisionEnabled= */ true, + /* modality= */ tests[i][0], + /* keyguardDisableFlags= */ tests[i][1]); + + assertNotNull(admin); + assertEquals(UserManager.DISALLOW_BIOMETRIC, admin.enforcedRestriction); + assertNull(admin.component); + } + } + + @Test + @RequiresFlagsDisabled(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS) public void testNoEnforcedAdmin_whenNoSupervisionComponent() { // Even if DPM flag exists, returns null EnforcedAdmin when no supervision component exists RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForCombination( @@ -106,4 +153,15 @@ public class ParentalControlsUtilsTest { DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); assertNull(admin); } + + @Test + @RequiresFlagsEnabled(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS) + public void testNoEnforcedAdmin_whenSupervisionIsDisabled() { + RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForSupervision( + /* supervisionEnabled= */ false, + TYPE_FINGERPRINT, + DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + + assertNull(admin); + } }