Add WearSafetySource (Without Listener)

Bug: 389841524
Test: TreeHugger
Test: manual
Test: atest SafetySourceBroadcastReceiverTest
Test: atest LockScreenSafetySourceTest
Test: atest ActiveUnlockStatusUtilsTest
Test: atest WearSafetySourceTest
Flag: com.android.settings.flags.biometrics_onboarding_education
Change-Id: I7f4b41bf33d1e0fb7988f756a466e4d80bcec25e
This commit is contained in:
Chun Zhang
2025-02-03 18:16:23 +00:00
parent f1d0ce612b
commit f7a128496c
13 changed files with 562 additions and 28 deletions

View File

@@ -1054,6 +1054,7 @@
<!-- Watch unlock enrollment and settings --><skip />
<!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
<string name ="security_settings_activeunlock_preference_title">Watch Unlock</string>
<string name="security_settings_activeunlock">Watch</string>
<!-- Introduction shown in face and fingerprint page to introduce the biometric feature. [CHAR LIMIT=NONE]-->
<string name="biometric_settings_intro_with_activeunlock">When you set up Face Unlock and Fingerprint Unlock, your phone will ask for your fingerprint when you wear a mask or are in a dark area.\n\nWatch Unlock is another convenient way to unlock your phone, for example, when your fingers are wet or face isn\u2019t recognized.</string>
<!-- Introduction shown in fingerprint page to explain that watch unlock can be used if fingerprint isn't recognized. [CHAR LIMIT=NONE]-->

View File

@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.utils.ThreadUtils;
@@ -76,17 +77,22 @@ public class ActiveUnlockContentListener {
mContentKey = contentKey;
String authority = new ActiveUnlockStatusUtils(mContext).getAuthority();
if (authority != null) {
mUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.appendPath(CONTENT_PROVIDER_PATH)
.build();
mUri = getUri(authority);
} else {
mUri = null;
}
}
/** Returns Active Unlock Uri. */
public static @NonNull Uri getUri(@NonNull String authority) {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.appendPath(CONTENT_PROVIDER_PATH)
.build();
}
/** Returns true if start listening for updates from the ContentProvider, false otherwise. */
public synchronized boolean subscribe() {
if (mSubscribed || mUri == null) {
@@ -123,25 +129,40 @@ public class ActiveUnlockContentListener {
Log.e(mLogTag, "Uri null when trying to fetch content");
return;
}
ContentResolver contentResolver = mContext.getContentResolver();
ContentProviderClient client = contentResolver.acquireContentProviderClient(mUri);
Bundle bundle;
try {
bundle = client.call(mMethodName, null /* arg */, null /* extras */);
} catch (RemoteException e) {
Log.e(mLogTag, "Failed to call contentProvider", e);
return;
} finally {
client.close();
}
if (bundle == null) {
Log.e(mLogTag, "Null bundle returned from contentProvider");
return;
}
String newValue = bundle.getString(mContentKey);
@Nullable String newValue = getContentFromUri(
mContext, mUri, mLogTag, mMethodName, mContentKey);
if (!TextUtils.equals(mContent, newValue)) {
mContent = newValue;
mContentChangedListener.onContentChanged(mContent);
}
}
/** Get the content from Uri. */
public static @Nullable String getContentFromUri(
@NonNull Context context,
@NonNull Uri uri,
@NonNull String logTag,
@NonNull String methodName,
@NonNull String contentKey) {
ContentResolver contentResolver = context.getContentResolver();
ContentProviderClient client = contentResolver.acquireContentProviderClient(uri);
@Nullable Bundle bundle = null;
try {
bundle = client.call(methodName, /* arg= */ null, /* extras = */ null);
} catch (RemoteException e) {
Log.e(logTag, "Failed to call contentProvider", e);
} finally {
client.close();
}
if (bundle == null) {
Log.e(logTag, "Null bundle returned from contentProvider");
return null;
}
return bundle.getString(contentKey);
}
}

View File

@@ -21,8 +21,8 @@ import android.content.Context;
/** Listens to device name updates from the content provider and fetches the latest value. */
public class ActiveUnlockDeviceNameListener {
private static final String TAG = "ActiveUnlockDeviceNameListener";
private static final String METHOD_NAME = "getDeviceName";
private static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
static final String METHOD_NAME = "getDeviceName";
static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
private final ActiveUnlockContentListener mActiveUnlockContentListener;
public ActiveUnlockDeviceNameListener(

View File

@@ -155,10 +155,17 @@ public class ActiveUnlockStatusUtils {
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
}
/**
* Returns the title of active unlock only.
*/
public @NonNull String getTitleForActiveUnlockOnly() {
return mContext.getString(R.string.security_settings_activeunlock);
}
/**
* Returns the title of the combined biometric settings entity when active unlock is enabled.
*/
public String getTitleForActiveUnlock() {
public @NonNull String getTitleForActiveUnlock() {
final boolean faceAllowed = Utils.hasFaceHardware(mContext);
final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
@@ -264,6 +271,30 @@ public class ActiveUnlockStatusUtils {
return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed));
}
/**
* Returns the summary from content provider.
*/
@Nullable
public static String getSummaryFromContentProvider(
@NonNull Context context, @NonNull String authority, @NonNull String logTag) {
return ActiveUnlockContentListener.getContentFromUri(
context, ActiveUnlockContentListener.getUri(authority), logTag,
ActiveUnlockSummaryListener.METHOD_NAME,
ActiveUnlockSummaryListener.SUMMARY_KEY);
}
/**
* Returns the device name from content provider.
*/
@Nullable
public static String getDeviceNameFromContentProvider(
@NonNull Context context, @NonNull String authority, @NonNull String logTag) {
return ActiveUnlockContentListener.getContentFromUri(
context, ActiveUnlockContentListener.getUri(authority), logTag,
ActiveUnlockDeviceNameListener.METHOD_NAME,
ActiveUnlockDeviceNameListener.DEVICE_NAME_KEY);
}
@StringRes
private static int getUseBiometricTitleRes(
boolean isFaceAllowed, boolean isFingerprintAllowed) {

View File

@@ -21,8 +21,8 @@ import android.content.Context;
/** Listens to summary updates from the content provider and fetches the latest value. */
public class ActiveUnlockSummaryListener {
private static final String TAG = "ActiveUnlockSummaryListener";
private static final String METHOD_NAME = "getSummary";
private static final String SUMMARY_KEY = "com.android.settings.summary";
static final String METHOD_NAME = "getSummary";
static final String SUMMARY_KEY = "com.android.settings.summary";
private final ActiveUnlockContentListener mContentListener;
public ActiveUnlockSummaryListener(

View File

@@ -66,7 +66,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
@VisibleForTesting
static final int CONFIRM_REQUEST = 2001;
private static final int CHOOSE_LOCK_REQUEST = 2002;
protected static final int ACTIVE_UNLOCK_REQUEST = 2003;
public static final int ACTIVE_UNLOCK_REQUEST = 2003;
@VisibleForTesting
static final int BIOMETRIC_AUTH_REQUEST = 2004;

View File

@@ -131,6 +131,7 @@ public final class LockScreenSafetySource {
if (Flags.biometricsOnboardingEducation()) {
FaceSafetySource.onBiometricsChanged(context);
FingerprintSafetySource.onBiometricsChanged(context);
WearSafetySource.onBiometricsChanged(context);
} else {
BiometricsSafetySource.onBiometricsChanged(context);
}

View File

@@ -86,6 +86,9 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
if (sourceIds.contains(FingerprintSafetySource.SAFETY_SOURCE_ID)) {
FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
}
if (sourceIds.contains(WearSafetySource.SAFETY_SOURCE_ID)) {
WearSafetySource.setSafetySourceData(context, safetyEvent);
}
}
private static void refreshAllSafetySources(Context context, SafetyEvent safetyEvent) {
@@ -95,5 +98,6 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
PrivateSpaceSafetySource.setSafetySourceData(context, safetyEvent);
FaceSafetySource.setSafetySourceData(context, safetyEvent);
FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
WearSafetySource.setSafetySourceData(context, safetyEvent);
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.safetycenter;
import static com.android.settings.biometrics.combination.BiometricsSettingsBase.ACTIVE_UNLOCK_REQUEST;
import android.app.PendingIntent;
import android.content.Context;
import android.os.UserManager;
import android.safetycenter.SafetyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.flags.Flags;
/** Wear Safety Source for Safety Center. */
public final class WearSafetySource {
private static final String TAG = "WearSafetySource";
public static final String SAFETY_SOURCE_ID = "AndroidWearUnlock";
private static boolean sIsTestingEnv = false;
private static String sSummaryForTesting = "";
private static boolean sHasEnrolledForTesting;
private WearSafetySource() {}
/** Sets test value for summary. */
@VisibleForTesting
public static void setSummaryForTesting(@NonNull String summary) {
sIsTestingEnv = true;
sSummaryForTesting = summary;
}
/** Sets test value for hasEnrolled. */
@VisibleForTesting
public static void setHasEnrolledForTesting(boolean hasEnrolled) {
sIsTestingEnv = true;
sHasEnrolledForTesting = hasEnrolled;
}
/** Sets biometric safety data for Safety Center. */
public static void setSafetySourceData(
@NonNull Context context, @NonNull SafetyEvent safetyEvent) {
if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
return;
}
if (!Flags.biometricsOnboardingEducation()) { // this source is effectively turned off
sendNullData(context, safetyEvent);
return;
}
// Handle private profile case.
UserManager userManager = UserManager.get(context);
if (android.os.Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
&& userManager.isPrivateProfile()) {
// SC always expects a response from the source if the broadcast has been sent for this
// source, therefore, we need to send a null SafetySourceData.
sendNullData(context, safetyEvent);
return;
}
ActiveUnlockStatusUtils activeUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
if (!userManager.isProfile() && activeUnlockStatusUtils.isAvailable()) {
boolean hasEnrolled = false;
String summary = "";
if (sIsTestingEnv) {
hasEnrolled = sHasEnrolledForTesting;
summary = sSummaryForTesting;
} else {
String authority = new ActiveUnlockStatusUtils(context).getAuthority();
hasEnrolled = getHasEnrolledFromContentProvider(context, authority);
summary = getSummaryFromContentProvider(context, authority);
}
BiometricSourcesUtils.setBiometricSafetySourceData(
SAFETY_SOURCE_ID,
context,
activeUnlockStatusUtils.getTitleForActiveUnlockOnly(),
summary,
PendingIntent.getActivity(context, ACTIVE_UNLOCK_REQUEST,
activeUnlockStatusUtils.getIntent(),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT),
/* enabled= */ true,
hasEnrolled,
safetyEvent);
return;
}
sendNullData(context, safetyEvent);
}
private static void sendNullData(Context context, SafetyEvent safetyEvent) {
SafetyCenterManagerWrapper.get()
.setSafetySourceData(
context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent);
}
/** Notifies Safety Center of a change in wear biometrics settings. */
public static void onBiometricsChanged(@NonNull Context context) {
setSafetySourceData(
context,
new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
.build());
}
private static boolean getHasEnrolledFromContentProvider(
@NonNull Context context, @Nullable String authority) {
if (authority == null) {
return false;
}
return ActiveUnlockStatusUtils.getDeviceNameFromContentProvider(context, authority, TAG)
!= null;
}
private static String getSummaryFromContentProvider(
@NonNull Context context, @Nullable String authority) {
if (authority == null) {
return "";
}
String summary = ActiveUnlockStatusUtils.getSummaryFromContentProvider(
context, authority, TAG);
if (summary == null) {
return "";
}
return summary;
}
}

View File

@@ -243,4 +243,11 @@ public class ActiveUnlockStatusUtilsTest {
.isEqualTo(mApplicationContext.getString(
R.string.biometric_settings_use_watch_for));
}
@Test
public void getTitleForActiveUnlockOnly_returnsTile() {
assertThat(mActiveUnlockStatusUtils.getTitleForActiveUnlockOnly())
.isEqualTo(mApplicationContext.getString(
R.string.security_settings_activeunlock));
}
}

View File

@@ -527,6 +527,9 @@ public class LockScreenSafetySourceTest {
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(), eq(FingerprintSafetySource.SAFETY_SOURCE_ID), any(), any());
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(), eq(WearSafetySource.SAFETY_SOURCE_ID), any(), any());
}
@Test

View File

@@ -245,6 +245,25 @@ public class SafetySourceBroadcastReceiverTest {
assertThat(captor.getValue()).isEqualTo(FaceSafetySource.SAFETY_SOURCE_ID);
}
@Test
public void onReceive_onRefresh_withWearUnlockSourceId_setsWearUnlockData() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
Intent intent =
new Intent()
.setAction(ACTION_REFRESH_SAFETY_SOURCES)
.putExtra(
EXTRA_REFRESH_SAFETY_SOURCE_IDS,
new String[] {WearSafetySource.SAFETY_SOURCE_ID})
.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_BROADCAST_ID);
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mSafetyCenterManagerWrapper, times(1))
.setSafetySourceData(any(), captor.capture(), any(), any());
assertThat(captor.getValue()).isEqualTo(WearSafetySource.SAFETY_SOURCE_ID);
}
@Test
public void onReceive_onRefresh_withFingerprintUnlockSourceId_setsFingerprintUnlockData() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
@@ -332,7 +351,7 @@ public class SafetySourceBroadcastReceiverTest {
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mSafetyCenterManagerWrapper, times(5))
verify(mSafetyCenterManagerWrapper, times(6))
.setSafetySourceData(any(), captor.capture(), any(), any());
List<String> safetySourceIdList = captor.getAllValues();
@@ -353,6 +372,11 @@ public class SafetySourceBroadcastReceiverTest {
.anyMatch(
id -> id.equals(FingerprintSafetySource.SAFETY_SOURCE_ID)))
.isTrue();
assertThat(
safetySourceIdList.stream()
.anyMatch(
id -> id.equals(WearSafetySource.SAFETY_SOURCE_ID)))
.isTrue();
assertThat(
safetySourceIdList.stream()
.anyMatch(

View File

@@ -0,0 +1,295 @@
/*
* 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.safetycenter;
import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
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 android.provider.DeviceConfig;
import android.provider.Settings;
import android.safetycenter.SafetyEvent;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceStatus;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.After;
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;
@RunWith(AndroidJUnit4.class)
public class WearSafetySourceTest {
private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class");
private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED =
new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
public static final String TARGET = "com.active.unlock.target";
public static final String PROVIDER = "com.active.unlock.provider";
public static final String TARGET_SETTING = "active_unlock_target";
public static final String PROVIDER_SETTING = "active_unlock_provider";
public static final String SUMMARY = "Wear Summary";
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mApplicationContext;
@Mock private PackageManager mPackageManager;
@Mock private DevicePolicyManager mDevicePolicyManager;
@Mock private FingerprintManager mFingerprintManager;
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
.thenReturn(COMPONENT_NAME);
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
.thenReturn(mFingerprintManager);
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext))
.thenReturn(mLockPatternUtils);
doReturn(true).when(mLockPatternUtils).isSecure(anyInt());
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
}
@After
public void tearDown() {
SafetyCenterManagerWrapper.sInstance = null;
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
verify(mSafetyCenterManagerWrapper, never())
.setSafetySourceData(any(), any(), any(), any());
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetySourceData_whenSeparateBiometricsFlagOff_setsNullData() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any());
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetySourceData_whenSafetyCenterIsEnabled_activeUnlockDisabled_setsNullData() {
disableActiveUnlock(mApplicationContext);
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any());
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetySourceData_setsDataWithCorrectSafetyEvent() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED));
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetySourceData_withWearEnabled_whenWearEnrolled_setsData() {
enableActiveUnlock(mApplicationContext);
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
WearSafetySource.setHasEnrolledForTesting(true);
WearSafetySource.setSummaryForTesting(SUMMARY);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
assertSafetySourceEnabledDataSet(
ResourcesUtils.getResourcesString(mApplicationContext,
"security_settings_activeunlock"),
SUMMARY);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
public void setSafetySourceData_withWearEnabled_whenWearNotEnrolled_setsData() {
enableActiveUnlock(mApplicationContext);
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
WearSafetySource.setHasEnrolledForTesting(false);
WearSafetySource.setSummaryForTesting(SUMMARY);
WearSafetySource.setSafetySourceData(
mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
assertSafetySourceDisabledDataSet(
ResourcesUtils.getResourcesString(mApplicationContext,
"security_settings_activeunlock"),
SUMMARY);
}
private static void disableActiveUnlock(Context context) {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_REMOTE_AUTH,
ActiveUnlockStatusUtils.CONFIG_FLAG_NAME,
/* value= */ null,
/* makeDefault=*/ false);
Settings.Secure.putString(context.getContentResolver(), TARGET_SETTING, null);
Settings.Secure.putString(context.getContentResolver(), PROVIDER_SETTING, null);
}
private static void enableActiveUnlock(Context context) {
Settings.Secure.putString(
context.getContentResolver(), TARGET_SETTING, TARGET);
Settings.Secure.putString(
context.getContentResolver(), PROVIDER_SETTING, PROVIDER);
PackageManager packageManager = context.getPackageManager();
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.applicationInfo = applicationInfo;
when(packageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = PROVIDER;
providerInfo.applicationInfo = applicationInfo;
when(packageManager.resolveContentProvider(anyString(), any())).thenReturn(providerInfo);
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_REMOTE_AUTH,
ActiveUnlockStatusUtils.CONFIG_FLAG_NAME,
"unlock_intent_layout",
false /* makeDefault */);
}
private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) {
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(),
eq(WearSafetySource.SAFETY_SOURCE_ID),
captor.capture(),
any());
SafetySourceData safetySourceData = captor.getValue();
SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle);
assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(safetySourceStatus.isEnabled()).isTrue();
assertThat(safetySourceStatus.getSeverityLevel())
.isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED);
Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent();
assertThat(clickIntent).isNotNull();
assertThat(clickIntent.getAction()).isEqualTo(TARGET);
}
private void assertSafetySourceEnabledDataSet(
String expectedTitle, String expectedSummary) {
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
verify(mSafetyCenterManagerWrapper)
.setSafetySourceData(
any(),
eq(WearSafetySource.SAFETY_SOURCE_ID),
captor.capture(),
any());
SafetySourceData safetySourceData = captor.getValue();
SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle);
assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(safetySourceStatus.isEnabled()).isTrue();
assertThat(safetySourceStatus.getSeverityLevel())
.isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION);
Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent();
assertThat(clickIntent).isNotNull();
}
}