Merge changes from topic "au-initial-commit-master"

* changes:
  Update text when Active Unlock is enabled.
  Update tile summary from ContentProvider.
  Add Active Unlock tile under face & fingerprint
  Add ActiveUnlock check when picking preference
This commit is contained in:
Derek Jedral
2023-01-31 03:24:22 +00:00
committed by Android (Google) Code Review
29 changed files with 1763 additions and 11 deletions

View File

@@ -25,6 +25,7 @@ import androidx.preference.Preference;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -37,11 +38,17 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
protected final int mProfileChallengeUserId;
private final BiometricNavigationUtils mBiometricNavigationUtils;
private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
/**
* @return true if the controller should be shown exclusively.
*/
protected abstract boolean isDeviceSupported();
/**
* @return true if the manager is not null and the hardware is detected.
*/
protected abstract boolean isDeviceSupported();
protected abstract boolean isHardwareSupported();
/**
* @return the summary text.
@@ -61,13 +68,21 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
.getLockPatternUtils(context);
mProfileChallengeUserId = Utils.getManagedProfileId(mUm, mUserId);
mBiometricNavigationUtils = new BiometricNavigationUtils(getUserId());
mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
}
@Override
public int getAvailabilityStatus() {
if (mActiveUnlockStatusUtils.isAvailable()) {
return getAvailabilityStatusWithWorkProfileCheck();
}
if (!isDeviceSupported()) {
return UNSUPPORTED_ON_DEVICE;
}
return getAvailabilityFromUserSupported();
}
private int getAvailabilityFromUserSupported() {
if (isUserSupported()) {
return AVAILABLE;
} else {
@@ -75,6 +90,21 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
}
}
// Since this code is flag guarded by mActiveUnlockStatusUtils.isAvailable(), we don't need to
// do another check here.
private int getAvailabilityStatusWithWorkProfileCheck() {
if (!isHardwareSupported()) {
// no hardware, never show
return UNSUPPORTED_ON_DEVICE;
}
if (!isDeviceSupported() && isWorkProfileController()) {
// hardware supported but work profile, don't show
return UNSUPPORTED_ON_DEVICE;
}
// hardware supported, not work profile, active unlock enabled
return getAvailabilityFromUserSupported();
}
@Override
public void updateState(Preference preference) {
if (!isAvailable()) {
@@ -105,4 +135,11 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
protected boolean isUserSupported() {
return true;
}
/**
* Returns true if the controller controls is used for work profile.
*/
protected boolean isWorkProfileController() {
return false;
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2023 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.biometrics.activeunlock;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.settingslib.utils.ThreadUtils;
/** Listens to updates from the content provider and fetches the latest value. */
public class ActiveUnlockContentListener {
/** Callback interface for updates to values from the ContentProvider. */
public interface OnContentChangedListener {
/**
* Called when the content observer has updated.
*
* @param newValue the new value retrieved from the ContentProvider.
**/
void onContentChanged(@Nullable String newValue);
}
private static final String CONTENT_PROVIDER_PATH = "getSummary";
private final Context mContext;
private final OnContentChangedListener mContentChangedListener;
@Nullable private final Uri mUri;
private final String mLogTag;
private final String mMethodName;
private final String mContentKey;
@Nullable private String mContent;
private boolean mSubscribed = false;
private ContentObserver mContentObserver =
new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
getContentFromUri();
}
};
ActiveUnlockContentListener(
Context context,
OnContentChangedListener listener,
String logTag,
String methodName,
String contentKey) {
mContext = context;
mContentChangedListener = listener;
mLogTag = logTag;
mMethodName = methodName;
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();
} else {
mUri = null;
}
}
/** Starts listening for updates from the ContentProvider, and fetches the current value. */
public synchronized void subscribe() {
if (mSubscribed && mUri != null) {
return;
}
mSubscribed = true;
mContext.getContentResolver().registerContentObserver(
mUri, true /* notifyForDescendants */, mContentObserver);
ThreadUtils.postOnBackgroundThread(
() -> {
getContentFromUri();
});
}
/** Stops listening for updates from the ContentProvider. */
public synchronized void unsubscribe() {
if (!mSubscribed && mUri != null) {
return;
}
mSubscribed = false;
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
}
/** Retrieves the most recently fetched value from the ContentProvider. */
@Nullable
public String getContent() {
return mContent;
}
private void getContentFromUri() {
if (mUri == null) {
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);
if (!TextUtils.equals(mContent, newValue)) {
mContent = newValue;
mContentChangedListener.onContentChanged(mContent);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 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.biometrics.activeunlock;
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";
private final ActiveUnlockContentListener mActiveUnlockContentListener;
public ActiveUnlockDeviceNameListener(
Context context, ActiveUnlockContentListener.OnContentChangedListener listener) {
mActiveUnlockContentListener = new ActiveUnlockContentListener(
context, listener, TAG, METHOD_NAME, DEVICE_NAME_KEY);
}
/** Returns whether a device is enrolled in Active Unlock. */
public boolean hasEnrolled() {
return mActiveUnlockContentListener.getContent() != null;
}
/** Subscribes to device name updates. */
public void subscribe() {
mActiveUnlockContentListener.subscribe();
}
/** Unsubscribes from device name updates. */
public void unsubscribe() {
mActiveUnlockContentListener.unsubscribe();
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2023 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.biometrics.activeunlock;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
import com.android.settingslib.RestrictedPreference;
/**
* Preference controller for active unlock settings within the biometrics settings page, that
* controls the ability to unlock the phone with watch authentication.
*/
public class ActiveUnlockStatusPreferenceController
extends BiometricStatusPreferenceController
implements LifecycleObserver, OnContentChangedListener {
/**
* Preference key.
*
* This must match the key found in security_settings_combined_biometric.xml
**/
public static final String KEY_ACTIVE_UNLOCK_SETTINGS = "biometric_active_unlock_settings";
@Nullable private RestrictedPreference mPreference;
@Nullable private PreferenceScreen mPreferenceScreen;
@Nullable private String mSummary;
private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
private final ActiveUnlockSummaryListener mActiveUnlockSummaryListener;
public ActiveUnlockStatusPreferenceController(@NonNull Context context) {
this(context, KEY_ACTIVE_UNLOCK_SETTINGS);
}
public ActiveUnlockStatusPreferenceController(
@NonNull Context context, @NonNull String key) {
super(context, key);
mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
mActiveUnlockSummaryListener = new ActiveUnlockSummaryListener(context, this);
}
/** Subscribes to update preference summary dynamically. */
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
mActiveUnlockSummaryListener.subscribe();
}
/** Resets the preference reference on resume. */
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (mPreferenceScreen != null) {
displayPreference(mPreferenceScreen);
}
}
/** Unsubscribes to prevent leaked listener. */
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
mActiveUnlockSummaryListener.unsubscribe();
}
@Override
public void onContentChanged(String newContent) {
mSummary = newContent;
if (mPreference != null) {
mPreference.setSummary(getSummaryText());
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceScreen = screen;
mPreference = screen.findPreference(mPreferenceKey);
updateState(mPreference);
}
@Override
public int getAvailabilityStatus() {
return mActiveUnlockStatusUtils.getAvailability();
}
@Override
protected boolean isDeviceSupported() {
// This should never be called, as getAvailabilityStatus() will return the exact value.
// However, this is an abstract method in BiometricStatusPreferenceController, and so
// needs to be overridden.
return mActiveUnlockStatusUtils.isAvailable();
}
@Override
protected boolean isHardwareSupported() {
// This should never be called, as getAvailabilityStatus() will return the exact value.
// However, this is an abstract method in BiometricStatusPreferenceController, and so
// needs to be overridden.
return Utils.hasFaceHardware(mContext) || Utils.hasFingerprintHardware(mContext);
}
@Override
protected String getSummaryText() {
if (mSummary == null) {
// return non-empty string to prevent re-sizing of the tile
return " ";
}
return mSummary;
}
@Override
protected String getSettingsClassName() {
// TODO(b/264813445): direct user to face & fingerprint setup
return null;
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright (C) 2023 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.biometrics.activeunlock;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.BasePreferenceController.AvailabilityStatus;
import java.util.List;
/** Utilities for active unlock details shared between Security Settings and Safety Center. */
public class ActiveUnlockStatusUtils {
/** The flag to determining whether active unlock in settings is enabled. */
public static final String CONFIG_FLAG_NAME = "active_unlock_in_settings";
/** Flag value that represents the layout for unlock intent should be used. */
public static final String UNLOCK_INTENT_LAYOUT = "unlock_intent_layout";
/** Flag value that represents the layout for biometric failure should be used. */
public static final String BIOMETRIC_FAILURE_LAYOUT = "biometric_failure_layout";
private static final String ACTIVE_UNLOCK_PROVIDER = "active_unlock_provider";
private static final String ACTIVE_UNLOCK_TARGET = "active_unlock_target";
private static final String TAG = "ActiveUnlockStatusUtils";
private final Context mContext;
private final ContentResolver mContentResolver;
public ActiveUnlockStatusUtils(@NonNull Context context) {
mContext = context;
mContentResolver = mContext.getContentResolver();
}
/** Returns whether the active unlock settings entity should be shown. */
public boolean isAvailable() {
return getAvailability() == BasePreferenceController.AVAILABLE;
}
/**
* Returns whether the active unlock layout with the unlock on intent configuration should be
* used.
*/
public boolean useUnlockIntentLayout() {
return isAvailable() && UNLOCK_INTENT_LAYOUT.equals(getFlagState());
}
/**
*
* Returns whether the active unlock layout with the unlock on biometric failure configuration
* should be used.
*/
public boolean useBiometricFailureLayout() {
return isAvailable() && BIOMETRIC_FAILURE_LAYOUT.equals(getFlagState());
}
/**
* Returns the authority used to fetch dynamic active unlock content.
*/
@Nullable
public String getAuthority() {
final String authority = Settings.Secure.getString(
mContext.getContentResolver(), ACTIVE_UNLOCK_PROVIDER);
if (authority == null) {
Log.i(TAG, "authority not set");
return null;
}
final List<PackageInfo> packageInfos =
mContext.getPackageManager().getInstalledPackages(
PackageManager.PackageInfoFlags.of(PackageManager.GET_PROVIDERS));
for (PackageInfo packageInfo : packageInfos) {
final ProviderInfo[] providers = packageInfo.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if (authority.equals(provider.authority) && isSystemApp(provider)) {
return authority;
}
}
}
}
Log.e(TAG, "authority not valid");
return null;
}
private static boolean isSystemApp(ComponentInfo componentInfo) {
final ApplicationInfo applicationInfo = componentInfo.applicationInfo;
if (applicationInfo == null) {
Log.e(TAG, "application info is null");
return false;
}
return applicationInfo.isSystemApp();
}
/**
* Returns the intent used to launch the active unlock activity.
*/
@Nullable
public Intent getIntent() {
final String targetAction = Settings.Secure.getString(
mContentResolver, ACTIVE_UNLOCK_TARGET);
if (targetAction == null) {
Log.i(TAG, "Target action not set");
return null;
}
final Intent intent = new Intent(targetAction);
final ActivityInfo activityInfo = intent.resolveActivityInfo(
mContext.getPackageManager(), PackageManager.MATCH_ALL);
if (activityInfo == null) {
Log.e(TAG, "Target activity not found");
return null;
}
if (!isSystemApp(activityInfo)) {
Log.e(TAG, "Target application is not system");
return null;
}
Log.i(TAG, "Target application is valid");
return intent;
}
/** Returns the availability status of the active unlock feature. */
@AvailabilityStatus
int getAvailability() {
if (!Utils.hasFingerprintHardware(mContext) && !Utils.hasFaceHardware(mContext)) {
return BasePreferenceController.UNSUPPORTED_ON_DEVICE;
}
if (!UNLOCK_INTENT_LAYOUT.equals(getFlagState())
&& !BIOMETRIC_FAILURE_LAYOUT.equals(getFlagState())) {
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
}
if (getAuthority() != null && getIntent() != null) {
return BasePreferenceController.AVAILABLE;
}
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
}
/**
* Returns the title of the combined biometric settings entity when active unlock is enabled.
*/
public String getTitleForActiveUnlock() {
final boolean faceAllowed = Utils.hasFaceHardware(mContext);
final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
}
@StringRes
private static int getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
if (isFaceAllowed && isFingerprintAllowed) {
return R.string.security_settings_biometric_preference_title;
} else if (isFaceAllowed) {
return R.string.security_settings_face_preference_title;
} else if (isFingerprintAllowed) {
return R.string.security_settings_fingerprint_preference_title;
} else {
// Default to original summary, but this case should never happen.
return R.string.security_settings_biometric_preference_title;
}
}
/**
* Returns the intro of the combined biometric settings entity when active unlock is enabled.
*/
public String getIntroForActiveUnlock() {
final boolean faceAllowed = Utils.hasFaceHardware(mContext);
final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
if (useBiometricFailureLayout()) {
int introRes = getIntroRes(faceAllowed, fingerprintAllowed);
return introRes == 0 ? "" : mContext.getString(introRes);
}
if (useUnlockIntentLayout() && (!faceAllowed || !fingerprintAllowed)) {
return "";
}
return mContext.getString(R.string.biometric_settings_intro);
}
@StringRes
private static int getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
if (isFaceAllowed && isFingerprintAllowed) {
return R.string.biometric_settings_intro_with_activeunlock;
} else if (isFaceAllowed) {
return R.string.biometric_settings_intro_with_face;
} else if (isFingerprintAllowed) {
return R.string.biometric_settings_intro_with_fingerprint;
} else {
return 0;
}
}
/**
* Returns the summary of the unlock device entity when active unlock is enabled.
*/
public String getUnlockDeviceSummaryForActiveUnlock() {
final boolean faceAllowed = Utils.hasFaceHardware(mContext);
final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
return mContext.getString(getUnlockDeviceSummaryRes(faceAllowed, fingerprintAllowed));
}
@StringRes
private static int getUnlockDeviceSummaryRes(
boolean isFaceAllowed, boolean isFingerprintAllowed) {
if (isFaceAllowed && isFingerprintAllowed) {
return R.string.biometric_settings_use_face_fingerprint_or_watch_preference_summary;
} else if (isFaceAllowed) {
return R.string.biometric_settings_use_face_or_watch_preference_summary;
} else if (isFingerprintAllowed) {
return R.string.biometric_settings_use_fingerprint_or_watch_preference_summary;
} else {
return R.string.biometric_settings_use_watch_preference_summary;
}
}
private static String getFlagState() {
return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 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.biometrics.activeunlock;
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";
private final ActiveUnlockContentListener mContentListener;
public ActiveUnlockSummaryListener(
Context context, ActiveUnlockContentListener.OnContentChangedListener listener) {
mContentListener = new ActiveUnlockContentListener(
context, listener, TAG, METHOD_NAME, SUMMARY_KEY);
}
/** Subscribes for summary updates. */
public void subscribe() {
mContentListener.subscribe();
}
/** Unsubscribes from summary updates. */
public void unsubscribe() {
mContentListener.unsubscribe();
}
}

View File

@@ -46,4 +46,9 @@ public class BiometricFaceProfileStatusPreferenceController extends
protected int getUserId() {
return mProfileChallengeUserId;
}
@Override
protected boolean isWorkProfileController() {
return true;
}
}

View File

@@ -39,6 +39,11 @@ public class BiometricFaceStatusPreferenceController extends FaceStatusPreferenc
@Override
protected boolean isDeviceSupported() {
return Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
return Utils.isMultipleBiometricsSupported(mContext) && isHardwareSupported();
}
@Override
protected boolean isHardwareSupported() {
return Utils.hasFaceHardware(mContext);
}
}

View File

@@ -46,4 +46,9 @@ public class BiometricFingerprintProfileStatusPreferenceController extends
protected int getUserId() {
return mProfileChallengeUserId;
}
@Override
protected boolean isWorkProfileController() {
return true;
}
}

View File

@@ -40,7 +40,11 @@ public class BiometricFingerprintStatusPreferenceController extends
@Override
protected boolean isDeviceSupported() {
return Utils.isMultipleBiometricsSupported(mContext)
&& Utils.hasFingerprintHardware(mContext);
return Utils.isMultipleBiometricsSupported(mContext) && isHardwareSupported();
}
@Override
protected boolean isHardwareSupported() {
return Utils.hasFingerprintHardware(mContext);
}
}

View File

@@ -24,6 +24,7 @@ import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -69,7 +70,10 @@ public class BiometricSettingsAppPreferenceController extends TogglePreferenceCo
@Override
public int getAvailabilityStatus() {
if (!Utils.isMultipleBiometricsSupported(mContext)) {
final ActiveUnlockStatusUtils activeUnlockStatusUtils =
new ActiveUnlockStatusUtils(mContext);
if (!Utils.isMultipleBiometricsSupported(mContext)
&& !activeUnlockStatusUtils.isAvailable()) {
return UNSUPPORTED_ON_DEVICE;
}
if (mFaceManager == null || mFingerprintManager == null) {

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.provider.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -63,9 +64,18 @@ public class BiometricSettingsKeyguardPreferenceController extends TogglePrefere
@Override
public int getAvailabilityStatus() {
final ActiveUnlockStatusUtils activeUnlockStatusUtils =
new ActiveUnlockStatusUtils(mContext);
if (activeUnlockStatusUtils.isAvailable()) {
return getAvailabilityFromRestrictingAdmin();
}
if (!Utils.isMultipleBiometricsSupported(mContext)) {
return UNSUPPORTED_ON_DEVICE;
}
return getAvailabilityFromRestrictingAdmin();
}
private int getAvailabilityFromRestrictingAdmin() {
return getRestrictingAdmin() != null ? DISABLED_FOR_USER : AVAILABLE;
}

View File

@@ -107,10 +107,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
launchChooseOrConfirmLock();
}
final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
if (unlockPhonePreference != null) {
unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
}
updateUnlockPhonePreferenceSummary();
final Preference useInAppsPreference = findPreference(getUseInAppsPreferenceKey());
if (useInAppsPreference != null) {
@@ -309,8 +306,15 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
}
}
protected void updateUnlockPhonePreferenceSummary() {
final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
if (unlockPhonePreference != null) {
unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
}
}
@NonNull
private String getUseAnyBiometricSummary() {
protected String getUseAnyBiometricSummary() {
boolean isFaceAllowed = mFaceManager != null && mFaceManager.isHardwareDetected();
boolean isFingerprintAllowed =
mFingerprintManager != null && mFingerprintManager.isHardwareDetected();

View File

@@ -62,4 +62,9 @@ public class CombinedBiometricProfileStatusPreferenceController extends
protected String getSettingsClassName() {
return mCombinedBiometricStatusUtils.getProfileSettingsClassName();
}
@Override
protected boolean isWorkProfileController() {
return true;
}
}

View File

@@ -17,8 +17,15 @@ package com.android.settings.biometrics.combination;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
import com.android.settings.biometrics.activeunlock.ActiveUnlockDeviceNameListener;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -32,6 +39,10 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings";
private static final String KEY_UNLOCK_PHONE = "biometric_settings_biometric_keyguard";
private static final String KEY_USE_IN_APPS = "biometric_settings_biometric_app";
private static final String KEY_INTRO_PREFERENCE = "biometric_intro";
private ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
@Nullable private ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
@Override
public void onAttach(Context context) {
@@ -40,6 +51,41 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
use(BiometricSettingsAppPreferenceController.class).setUserId(mUserId);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(getActivity());
if (mActiveUnlockStatusUtils.isAvailable()) {
updateUiForActiveUnlock();
}
}
private void updateUiForActiveUnlock() {
OnContentChangedListener listener = new OnContentChangedListener() {
@Override
public void onContentChanged(String newValue) {
updateUnlockPhonePreferenceSummary();
}
};
mActiveUnlockDeviceNameListener =
new ActiveUnlockDeviceNameListener(getActivity(), listener);
mActiveUnlockDeviceNameListener.subscribe();
final Preference introPreference = findPreference(KEY_INTRO_PREFERENCE);
if (introPreference != null) {
introPreference.setTitle(mActiveUnlockStatusUtils.getIntroForActiveUnlock());
}
getActivity().setTitle(mActiveUnlockStatusUtils.getTitleForActiveUnlock());
}
@Override
public void onDestroy() {
if (mActiveUnlockDeviceNameListener != null) {
mActiveUnlockDeviceNameListener.unsubscribe();
}
super.onDestroy();
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.security_settings_combined_biometric;
@@ -75,6 +121,16 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
return SettingsEnums.COMBINED_BIOMETRIC;
}
@Override
protected String getUseAnyBiometricSummary() {
// either Active Unlock is not enabled or no device is enrolled.
if (mActiveUnlockDeviceNameListener == null
|| !mActiveUnlockDeviceNameListener.hasEnrolled()) {
return super.getUseAnyBiometricSummary();
}
return mActiveUnlockStatusUtils.getUnlockDeviceSummaryForActiveUnlock();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric);
}

View File

@@ -25,6 +25,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -84,6 +85,11 @@ public class CombinedBiometricStatusPreferenceController extends
return mCombinedBiometricStatusUtils.isAvailable();
}
@Override
protected boolean isHardwareSupported() {
return Utils.hasFaceHardware(mContext) || Utils.hasFingerprintHardware(mContext);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);

View File

@@ -84,4 +84,9 @@ public class FaceProfileStatusPreferenceController extends FaceStatusPreferenceC
mContext.getResources().getString(
R.string.security_settings_face_profile_preference_title)));
}
@Override
protected boolean isWorkProfileController() {
return true;
}
}

View File

@@ -86,6 +86,11 @@ public class FaceStatusPreferenceController extends BiometricStatusPreferenceCon
return mFaceStatusUtils.isAvailable();
}
@Override
protected boolean isHardwareSupported() {
return Utils.hasFaceHardware(mContext);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);

View File

@@ -53,4 +53,9 @@ public class FingerprintProfileStatusPreferenceController
protected int getUserId() {
return mProfileChallengeUserId;
}
@Override
protected boolean isWorkProfileController() {
return true;
}
}

View File

@@ -86,6 +86,11 @@ public class FingerprintStatusPreferenceController extends BiometricStatusPrefer
return mFingerprintStatusUtils.isAvailable();
}
@Override
protected boolean isHardwareSupported() {
return Utils.hasFingerprintHardware(mContext);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);