1 /* 2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.safetycenter; 18 19 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.any; 28 import static org.mockito.Mockito.anyInt; 29 import static org.mockito.Mockito.anyString; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.app.admin.DevicePolicyManager; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.pm.ActivityInfo; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.PackageManager; 42 import android.content.pm.ProviderInfo; 43 import android.content.pm.ResolveInfo; 44 import android.hardware.fingerprint.FingerprintManager; 45 import android.os.UserHandle; 46 import android.platform.test.annotations.RequiresFlagsDisabled; 47 import android.platform.test.annotations.RequiresFlagsEnabled; 48 import android.platform.test.flag.junit.CheckFlagsRule; 49 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 50 import android.provider.DeviceConfig; 51 import android.provider.Settings; 52 import android.safetycenter.SafetyEvent; 53 import android.safetycenter.SafetySourceData; 54 import android.safetycenter.SafetySourceStatus; 55 56 import androidx.test.core.app.ApplicationProvider; 57 import androidx.test.ext.junit.runners.AndroidJUnit4; 58 59 import com.android.internal.widget.LockPatternUtils; 60 import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils; 61 import com.android.settings.flags.Flags; 62 import com.android.settings.testutils.FakeFeatureFactory; 63 import com.android.settings.testutils.ResourcesUtils; 64 65 import org.junit.After; 66 import org.junit.Before; 67 import org.junit.Rule; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 import org.mockito.ArgumentCaptor; 71 import org.mockito.Mock; 72 import org.mockito.MockitoAnnotations; 73 74 @RunWith(AndroidJUnit4.class) 75 public class WearSafetySourceTest { 76 77 private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class"); 78 private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId()); 79 private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED = 80 new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(); 81 public static final String TARGET = "com.active.unlock.target"; 82 public static final String PROVIDER = "com.active.unlock.provider"; 83 public static final String TARGET_SETTING = "active_unlock_target"; 84 public static final String PROVIDER_SETTING = "active_unlock_provider"; 85 public static final String SUMMARY = "Wear Summary"; 86 87 @Rule 88 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 89 90 private Context mApplicationContext; 91 92 @Mock private PackageManager mPackageManager; 93 @Mock private DevicePolicyManager mDevicePolicyManager; 94 @Mock private FingerprintManager mFingerprintManager; 95 @Mock private LockPatternUtils mLockPatternUtils; 96 @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; 97 98 @Before setUp()99 public void setUp() { 100 MockitoAnnotations.initMocks(this); 101 mApplicationContext = spy(ApplicationProvider.getApplicationContext()); 102 when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager); 103 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); 104 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); 105 when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE)) 106 .thenReturn(COMPONENT_NAME); 107 when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE)) 108 .thenReturn(mFingerprintManager); 109 when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) 110 .thenReturn(mDevicePolicyManager); 111 FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); 112 when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext)) 113 .thenReturn(mLockPatternUtils); 114 doReturn(true).when(mLockPatternUtils).isSecure(anyInt()); 115 SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; 116 } 117 118 @After tearDown()119 public void tearDown() { 120 SafetyCenterManagerWrapper.sInstance = null; 121 } 122 123 @Test 124 @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData()125 public void setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData() { 126 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false); 127 128 WearSafetySource.setSafetySourceData( 129 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 130 131 verify(mSafetyCenterManagerWrapper, never()) 132 .setSafetySourceData(any(), any(), any(), any()); 133 } 134 135 @Test 136 @RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetySourceData_whenSeparateBiometricsFlagOff_setsNullData()137 public void setSafetySourceData_whenSeparateBiometricsFlagOff_setsNullData() { 138 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); 139 140 WearSafetySource.setSafetySourceData( 141 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 142 143 verify(mSafetyCenterManagerWrapper) 144 .setSafetySourceData( 145 any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any()); 146 } 147 148 @Test 149 @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetySourceData_whenSafetyCenterIsEnabled_activeUnlockDisabled_setsNullData()150 public void setSafetySourceData_whenSafetyCenterIsEnabled_activeUnlockDisabled_setsNullData() { 151 disableActiveUnlock(mApplicationContext); 152 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); 153 154 WearSafetySource.setSafetySourceData( 155 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 156 157 verify(mSafetyCenterManagerWrapper) 158 .setSafetySourceData( 159 any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any()); 160 } 161 162 @Test 163 @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetySourceData_setsDataWithCorrectSafetyEvent()164 public void setSafetySourceData_setsDataWithCorrectSafetyEvent() { 165 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); 166 167 WearSafetySource.setSafetySourceData( 168 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 169 170 verify(mSafetyCenterManagerWrapper) 171 .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED)); 172 } 173 174 175 @Test 176 @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetySourceData_withWearEnabled_whenWearEnrolled_setsData()177 public void setSafetySourceData_withWearEnabled_whenWearEnrolled_setsData() { 178 enableActiveUnlock(mApplicationContext); 179 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); 180 when(mFingerprintManager.isHardwareDetected()).thenReturn(true); 181 when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); 182 183 WearSafetySource.setHasEnrolledForTesting(true); 184 WearSafetySource.setSummaryForTesting(SUMMARY); 185 186 WearSafetySource.setSafetySourceData( 187 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 188 189 assertSafetySourceEnabledDataSet( 190 ResourcesUtils.getResourcesString(mApplicationContext, 191 "security_settings_activeunlock"), 192 SUMMARY); 193 } 194 195 @Test 196 @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) setSafetySourceData_withWearEnabled_whenWearNotEnrolled_setsData()197 public void setSafetySourceData_withWearEnabled_whenWearNotEnrolled_setsData() { 198 enableActiveUnlock(mApplicationContext); 199 when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); 200 when(mFingerprintManager.isHardwareDetected()).thenReturn(true); 201 when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); 202 when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); 203 204 WearSafetySource.setHasEnrolledForTesting(false); 205 WearSafetySource.setSummaryForTesting(SUMMARY); 206 207 WearSafetySource.setSafetySourceData( 208 mApplicationContext, EVENT_SOURCE_STATE_CHANGED); 209 210 assertSafetySourceDisabledDataSet( 211 ResourcesUtils.getResourcesString(mApplicationContext, 212 "security_settings_activeunlock"), 213 SUMMARY); 214 } 215 disableActiveUnlock(Context context)216 private static void disableActiveUnlock(Context context) { 217 DeviceConfig.setProperty( 218 DeviceConfig.NAMESPACE_REMOTE_AUTH, 219 ActiveUnlockStatusUtils.CONFIG_FLAG_NAME, 220 /* value= */ null, 221 /* makeDefault=*/ false); 222 Settings.Secure.putString(context.getContentResolver(), TARGET_SETTING, null); 223 Settings.Secure.putString(context.getContentResolver(), PROVIDER_SETTING, null); 224 } 225 enableActiveUnlock(Context context)226 private static void enableActiveUnlock(Context context) { 227 Settings.Secure.putString( 228 context.getContentResolver(), TARGET_SETTING, TARGET); 229 Settings.Secure.putString( 230 context.getContentResolver(), PROVIDER_SETTING, PROVIDER); 231 232 PackageManager packageManager = context.getPackageManager(); 233 ApplicationInfo applicationInfo = new ApplicationInfo(); 234 applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; 235 236 ResolveInfo resolveInfo = new ResolveInfo(); 237 resolveInfo.activityInfo = new ActivityInfo(); 238 resolveInfo.activityInfo.applicationInfo = applicationInfo; 239 when(packageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); 240 241 ProviderInfo providerInfo = new ProviderInfo(); 242 providerInfo.authority = PROVIDER; 243 providerInfo.applicationInfo = applicationInfo; 244 when(packageManager.resolveContentProvider(anyString(), any())).thenReturn(providerInfo); 245 246 DeviceConfig.setProperty( 247 DeviceConfig.NAMESPACE_REMOTE_AUTH, 248 ActiveUnlockStatusUtils.CONFIG_FLAG_NAME, 249 "unlock_intent_layout", 250 false /* makeDefault */); 251 } 252 assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary)253 private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) { 254 ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class); 255 verify(mSafetyCenterManagerWrapper) 256 .setSafetySourceData( 257 any(), 258 eq(WearSafetySource.SAFETY_SOURCE_ID), 259 captor.capture(), 260 any()); 261 SafetySourceData safetySourceData = captor.getValue(); 262 SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); 263 264 assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); 265 assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); 266 assertThat(safetySourceStatus.isEnabled()).isTrue(); 267 assertThat(safetySourceStatus.getSeverityLevel()) 268 .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); 269 270 Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); 271 assertThat(clickIntent).isNotNull(); 272 assertThat(clickIntent.getAction()).isEqualTo(TARGET); 273 } 274 assertSafetySourceEnabledDataSet( String expectedTitle, String expectedSummary)275 private void assertSafetySourceEnabledDataSet( 276 String expectedTitle, String expectedSummary) { 277 ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class); 278 verify(mSafetyCenterManagerWrapper) 279 .setSafetySourceData( 280 any(), 281 eq(WearSafetySource.SAFETY_SOURCE_ID), 282 captor.capture(), 283 any()); 284 SafetySourceData safetySourceData = captor.getValue(); 285 SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); 286 287 assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); 288 assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); 289 assertThat(safetySourceStatus.isEnabled()).isTrue(); 290 assertThat(safetySourceStatus.getSeverityLevel()) 291 .isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION); 292 Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); 293 assertThat(clickIntent).isNotNull(); 294 } 295 } 296