• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.Activity.RESULT_OK;
20 
21 import static com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST;
22 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
23 
24 import android.app.settings.SettingsEnums;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.hardware.face.FaceManager;
28 import android.os.Bundle;
29 import android.os.UserHandle;
30 import android.os.UserManager;
31 import android.provider.SearchIndexableResource;
32 import android.util.Log;
33 
34 import androidx.preference.Preference;
35 
36 import com.android.settings.R;
37 import com.android.settings.SettingsActivity;
38 import com.android.settings.Utils;
39 import com.android.settings.dashboard.DashboardFragment;
40 import com.android.settings.password.ChooseLockSettingsHelper;
41 import com.android.settings.search.BaseSearchIndexProvider;
42 import com.android.settingslib.core.AbstractPreferenceController;
43 import com.android.settingslib.core.lifecycle.Lifecycle;
44 import com.android.settingslib.search.SearchIndexable;
45 
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.List;
49 
50 /**
51  * Settings screen for face authentication.
52  */
53 @SearchIndexable
54 public class FaceSettings extends DashboardFragment {
55 
56     private static final String TAG = "FaceSettings";
57     private static final String KEY_TOKEN = "hw_auth_token";
58 
59     private UserManager mUserManager;
60     private FaceManager mFaceManager;
61     private int mUserId;
62     private byte[] mToken;
63     private FaceSettingsAttentionPreferenceController mAttentionController;
64     private FaceSettingsRemoveButtonPreferenceController mRemoveController;
65     private FaceSettingsEnrollButtonPreferenceController mEnrollController;
66     private List<AbstractPreferenceController> mControllers;
67 
68     private List<Preference> mTogglePreferences;
69     private Preference mRemoveButton;
70     private Preference mEnrollButton;
71 
72     private final FaceSettingsRemoveButtonPreferenceController.Listener mRemovalListener = () -> {
73 
74         // Disable the toggles until the user re-enrolls
75         for (Preference preference : mTogglePreferences) {
76             preference.setEnabled(false);
77         }
78 
79         // Hide the "remove" button and show the "set up face authentication" button.
80         mRemoveButton.setVisible(false);
81         mEnrollButton.setVisible(true);
82     };
83 
isAvailable(Context context)84     public static boolean isAvailable(Context context) {
85         FaceManager manager = Utils.getFaceManagerOrNull(context);
86         return manager != null && manager.isHardwareDetected();
87     }
88 
89     @Override
getMetricsCategory()90     public int getMetricsCategory() {
91         return SettingsEnums.FACE;
92     }
93 
94     @Override
getPreferenceScreenResId()95     protected int getPreferenceScreenResId() {
96         return R.xml.security_settings_face;
97     }
98 
99     @Override
getLogTag()100     protected String getLogTag() {
101         return TAG;
102     }
103 
104     @Override
onSaveInstanceState(Bundle outState)105     public void onSaveInstanceState(Bundle outState) {
106         super.onSaveInstanceState(outState);
107         outState.putByteArray(KEY_TOKEN, mToken);
108     }
109 
110     @Override
onCreate(Bundle savedInstanceState)111     public void onCreate(Bundle savedInstanceState) {
112         super.onCreate(savedInstanceState);
113 
114         mToken = getIntent().getByteArrayExtra(KEY_TOKEN);
115         mUserManager = getPrefContext().getSystemService(UserManager.class);
116         mFaceManager = getPrefContext().getSystemService(FaceManager.class);
117         mUserId = getActivity().getIntent().getIntExtra(
118                 Intent.EXTRA_USER_ID, UserHandle.myUserId());
119 
120         Preference keyguardPref = findPreference(FaceSettingsKeyguardPreferenceController.KEY);
121         Preference appPref = findPreference(FaceSettingsAppPreferenceController.KEY);
122         Preference attentionPref = findPreference(FaceSettingsAttentionPreferenceController.KEY);
123         Preference confirmPref = findPreference(FaceSettingsConfirmPreferenceController.KEY);
124         mTogglePreferences = new ArrayList<>(
125                 Arrays.asList(keyguardPref, appPref, attentionPref, confirmPref));
126 
127         mRemoveButton = findPreference(FaceSettingsRemoveButtonPreferenceController.KEY);
128         mEnrollButton = findPreference(FaceSettingsEnrollButtonPreferenceController.KEY);
129 
130         // There is no better way to do this :/
131         for (AbstractPreferenceController controller : mControllers) {
132             if (controller instanceof  FaceSettingsPreferenceController) {
133                 ((FaceSettingsPreferenceController) controller).setUserId(mUserId);
134             } else if (controller instanceof FaceSettingsEnrollButtonPreferenceController) {
135                 ((FaceSettingsEnrollButtonPreferenceController) controller).setUserId(mUserId);
136             }
137         }
138         mRemoveController.setUserId(mUserId);
139 
140         // Don't show keyguard controller for work profile settings.
141         if (mUserManager.isManagedProfile(mUserId)) {
142             removePreference(FaceSettingsKeyguardPreferenceController.KEY);
143         }
144 
145         if (savedInstanceState != null) {
146             mToken = savedInstanceState.getByteArray(KEY_TOKEN);
147         }
148 
149         if (mToken == null) {
150             final long challenge = mFaceManager.generateChallenge();
151             ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);
152             if (!helper.launchConfirmationActivity(CONFIRM_REQUEST,
153                     getString(R.string.security_settings_face_preference_title),
154                     null, null, challenge, mUserId)) {
155                 Log.e(TAG, "Password not set");
156                 finish();
157             }
158         }
159     }
160 
161     @Override
onResume()162     public void onResume() {
163         super.onResume();
164         if (mToken != null) {
165             mAttentionController.setToken(mToken);
166             mEnrollController.setToken(mToken);
167         }
168 
169         final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId);
170         mEnrollButton.setVisible(!hasEnrolled);
171         mRemoveButton.setVisible(hasEnrolled);
172     }
173 
174     @Override
onActivityResult(int requestCode, int resultCode, Intent data)175     public void onActivityResult(int requestCode, int resultCode, Intent data) {
176         super.onActivityResult(requestCode, resultCode, data);
177         if (requestCode == CONFIRM_REQUEST) {
178             if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
179                 mFaceManager.setActiveUser(mUserId);
180                 // The pin/pattern/password was set.
181                 if (data != null) {
182                     mToken = data.getByteArrayExtra(
183                             ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
184                     if (mToken != null) {
185                         mAttentionController.setToken(mToken);
186                         mEnrollController.setToken(mToken);
187                     }
188                 }
189             }
190         }
191 
192         if (mToken == null) {
193             // Didn't get an authentication, finishing
194             getActivity().finish();
195         }
196     }
197 
198     @Override
onDestroy()199     public void onDestroy() {
200         super.onDestroy();
201         if (getActivity().isFinishing()) {
202             final int result = mFaceManager.revokeChallenge();
203             if (result < 0) {
204                 Log.w(TAG, "revokeChallenge failed, result: " + result);
205             }
206         }
207     }
208 
209     @Override
createPreferenceControllers(Context context)210     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
211         if (!isAvailable(context)) {
212             return null;
213         }
214         mControllers = buildPreferenceControllers(context, getSettingsLifecycle());
215         // There's no great way of doing this right now :/
216         for (AbstractPreferenceController controller : mControllers) {
217             if (controller instanceof FaceSettingsAttentionPreferenceController) {
218                 mAttentionController = (FaceSettingsAttentionPreferenceController) controller;
219             } else if (controller instanceof FaceSettingsRemoveButtonPreferenceController) {
220                 mRemoveController = (FaceSettingsRemoveButtonPreferenceController) controller;
221                 mRemoveController.setListener(mRemovalListener);
222                 mRemoveController.setActivity((SettingsActivity) getActivity());
223             } else if (controller instanceof FaceSettingsEnrollButtonPreferenceController) {
224                 mEnrollController = (FaceSettingsEnrollButtonPreferenceController) controller;
225                 mEnrollController.setActivity((SettingsActivity) getActivity());
226             }
227         }
228 
229         return mControllers;
230     }
231 
buildPreferenceControllers(Context context, Lifecycle lifecycle)232     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
233             Lifecycle lifecycle) {
234         final List<AbstractPreferenceController> controllers = new ArrayList<>();
235         controllers.add(new FaceSettingsVideoPreferenceController(context));
236         controllers.add(new FaceSettingsKeyguardPreferenceController(context));
237         controllers.add(new FaceSettingsAppPreferenceController(context));
238         controllers.add(new FaceSettingsAttentionPreferenceController(context));
239         controllers.add(new FaceSettingsRemoveButtonPreferenceController(context));
240         controllers.add(new FaceSettingsFooterPreferenceController(context));
241         controllers.add(new FaceSettingsConfirmPreferenceController(context));
242         controllers.add(new FaceSettingsEnrollButtonPreferenceController(context));
243         return controllers;
244     }
245 
246     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
247             new BaseSearchIndexProvider() {
248                 @Override
249                 public List<SearchIndexableResource> getXmlResourcesToIndex(
250                         Context context, boolean enabled) {
251                     final SearchIndexableResource sir = new SearchIndexableResource(context);
252                     sir.xmlResId = R.xml.security_settings_face;
253                     return Arrays.asList(sir);
254                 }
255 
256                 @Override
257                 public List<AbstractPreferenceController> createPreferenceControllers(
258                         Context context) {
259                     if (isAvailable(context)) {
260                         return buildPreferenceControllers(context, null /* lifecycle */);
261                     } else {
262                         return null;
263                     }
264                 }
265 
266                 @Override
267                 protected boolean isPageSearchEnabled(Context context) {
268                     return isAvailable(context);
269                 }
270             };
271 
272 }
273