1 /*
2  * Copyright (C) 2018 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.biometrics.face;
18 
19 import static android.hardware.biometrics.BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION;
20 
21 import android.content.Context;
22 import android.hardware.face.FaceManager;
23 import android.hardware.face.FaceManager.GetFeatureCallback;
24 import android.hardware.face.FaceManager.SetFeatureCallback;
25 import android.provider.Settings;
26 
27 import androidx.annotation.Nullable;
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceScreen;
30 import androidx.preference.TwoStatePreference;
31 
32 import com.android.settings.R;
33 import com.android.settings.Utils;
34 
35 /**
36  * Preference controller that manages the ability to use face authentication with/without
37  * user attention. See {@link FaceManager#setRequireAttention(boolean, byte[])}.
38  */
39 public class FaceSettingsAttentionPreferenceController extends FaceSettingsPreferenceController {
40 
41     public static final String KEY = "security_settings_face_require_attention";
42 
43     private byte[] mToken;
44     private FaceManager mFaceManager;
45     private TwoStatePreference mPreference;
46 
47     private final SetFeatureCallback mSetFeatureCallback = new SetFeatureCallback() {
48         @Override
49         public void onCompleted(boolean success, int feature) {
50             if (feature == FEATURE_REQUIRE_ATTENTION) {
51                 mPreference.setEnabled(true);
52                 if (!success) {
53                     mPreference.setChecked(!mPreference.isChecked());
54                 } else {
55                     Settings.Secure.putIntForUser(mContext.getContentResolver(),
56                             Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
57                             mPreference.isChecked() ? 1 : 0, getUserId());
58                 }
59             }
60         }
61     };
62 
63     private final GetFeatureCallback mGetFeatureCallback = new GetFeatureCallback() {
64         @Override
65         public void onCompleted(boolean success, int[] features, boolean[] featureState) {
66             boolean requireAttentionEnabled = false;
67             for (int i = 0; i < features.length; i++) {
68                 if (features[i] == FEATURE_REQUIRE_ATTENTION) {
69                     requireAttentionEnabled = featureState[i];
70                 }
71             }
72             mPreference.setChecked(requireAttentionEnabled);
73             if (getRestrictingAdmin() != null) {
74                 mPreference.setEnabled(false);
75             } else {
76                 mPreference.setEnabled(success);
77             }
78         }
79     };
80 
FaceSettingsAttentionPreferenceController(Context context, String preferenceKey)81     public FaceSettingsAttentionPreferenceController(Context context, String preferenceKey) {
82         super(context, preferenceKey);
83         mFaceManager = Utils.getFaceManagerOrNull(context);
84     }
85 
FaceSettingsAttentionPreferenceController(Context context)86     public FaceSettingsAttentionPreferenceController(Context context) {
87         this(context, KEY);
88     }
89 
setToken(byte[] token)90     public void setToken(byte[] token) {
91         mToken = token;
92     }
93 
94     /**
95      * Displays preference in this controller.
96      */
97     @Override
displayPreference(PreferenceScreen screen)98     public void displayPreference(PreferenceScreen screen) {
99         super.displayPreference(screen);
100         mPreference = screen.findPreference(KEY);
101     }
102 
103     @Override
updateState(@ullable Preference preference)104     public void updateState(@Nullable Preference preference) {
105         if (preference == null) {
106             return;
107         }
108         super.updateState(preference);
109         if (Utils.isPrivateProfile(getUserId(), mContext)) {
110             preference.setSummary(mContext.getString(
111                     R.string.private_space_face_settings_require_attention_details));
112         }
113     }
114 
115     @Override
isChecked()116     public boolean isChecked() {
117         if (!FaceSettings.isFaceHardwareDetected(mContext)) {
118             return true;
119         }
120         // Set to disabled until we know the true value.
121         mPreference.setEnabled(false);
122         mFaceManager.getFeature(getUserId(), FEATURE_REQUIRE_ATTENTION,
123                 mGetFeatureCallback);
124 
125         // Ideally returns a cached value.
126         return true;
127     }
128 
129     @Override
setChecked(boolean isChecked)130     public boolean setChecked(boolean isChecked) {
131         // Optimistically update state and set to disabled until we know it succeeded.
132         mPreference.setEnabled(false);
133         mPreference.setChecked(isChecked);
134 
135         mFaceManager.setFeature(getUserId(), FEATURE_REQUIRE_ATTENTION,
136                 isChecked, mToken, mSetFeatureCallback);
137         return true;
138     }
139 
140     @Override
getAvailabilityStatus()141     public int getAvailabilityStatus() {
142         return AVAILABLE;
143     }
144 }
145