1 /*
2  * Copyright (C) 2015 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.password;
18 
19 import android.content.Intent;
20 import android.os.AsyncTask;
21 import android.os.Bundle;
22 import android.os.UserHandle;
23 import android.util.Log;
24 import android.util.Pair;
25 import android.widget.Toast;
26 
27 import androidx.annotation.VisibleForTesting;
28 import androidx.fragment.app.Fragment;
29 
30 import com.android.internal.widget.LockPatternUtils;
31 import com.android.internal.widget.LockscreenCredential;
32 import com.android.internal.widget.VerifyCredentialResponse;
33 import com.android.settings.R;
34 import com.android.settings.safetycenter.LockScreenSafetySource;
35 
36 /**
37  * An invisible retained worker fragment to track the AsyncWork that saves (and optionally
38  * verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
39  */
40 public class SaveAndFinishWorker extends Fragment {
41     private static final String TAG = "SaveAndFinishWorker";
42 
43     private Listener mListener;
44     private boolean mFinished;
45     private Intent mResultData;
46 
47     private LockPatternUtils mUtils;
48     private boolean mRequestGatekeeperPassword;
49     private boolean mRequestWriteRepairModePassword;
50     private boolean mWasSecureBefore;
51     private int mUserId;
52     private int mUnificationProfileId = UserHandle.USER_NULL;
53     private LockscreenCredential mUnificationProfileCredential;
54     private LockscreenCredential mChosenCredential;
55     private LockscreenCredential mCurrentCredential;
56 
57     private boolean mBlocking;
58 
59     @Override
onCreate(Bundle savedInstanceState)60     public void onCreate(Bundle savedInstanceState) {
61         super.onCreate(savedInstanceState);
62         setRetainInstance(true);
63     }
64 
setListener(Listener listener)65     public SaveAndFinishWorker setListener(Listener listener) {
66         if (mListener == listener) {
67             return this;
68         }
69 
70         mListener = listener;
71         if (mFinished && mListener != null) {
72             mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
73         }
74         return this;
75     }
76 
77     @VisibleForTesting
prepare(LockPatternUtils utils, LockscreenCredential chosenCredential, LockscreenCredential currentCredential, int userId)78     void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential,
79             LockscreenCredential currentCredential, int userId) {
80         mUtils = utils;
81         mUserId = userId;
82         // This will be a no-op for non managed profiles.
83         mWasSecureBefore = mUtils.isSecure(mUserId);
84         mFinished = false;
85         mResultData = null;
86 
87         mChosenCredential = chosenCredential;
88         mCurrentCredential = currentCredential != null ? currentCredential
89                 : LockscreenCredential.createNone();
90     }
91 
start(LockPatternUtils utils, LockscreenCredential chosenCredential, LockscreenCredential currentCredential, int userId)92     public void start(LockPatternUtils utils, LockscreenCredential chosenCredential,
93             LockscreenCredential currentCredential, int userId) {
94         prepare(utils, chosenCredential, currentCredential, userId);
95         if (mBlocking) {
96             finish(saveAndVerifyInBackground().second);
97         } else {
98             new Task().execute();
99         }
100     }
101 
102     /**
103      * Executes the save and verify work in background.
104      * @return pair where the first is a boolean confirming whether the change was successful or not
105      * and second is the Intent which has the challenge token or is null.
106      */
107     @VisibleForTesting
saveAndVerifyInBackground()108     Pair<Boolean, Intent> saveAndVerifyInBackground() {
109         final int userId = mUserId;
110         try {
111             if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) {
112                 return Pair.create(false, null);
113             }
114         } catch (RuntimeException e) {
115             Log.e(TAG, "Failed to set lockscreen credential", e);
116             return Pair.create(false, null);
117         }
118 
119         unifyProfileCredentialIfRequested();
120 
121         @LockPatternUtils.VerifyFlag int flags = 0;
122         if (mRequestGatekeeperPassword) {
123             // If a Gatekeeper Password was requested, invoke the LockSettingsService code
124             // path to return a Gatekeeper Password based on the credential that the user
125             // chose. This should only be run if the credential was successfully set.
126             flags |= LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
127         }
128         if (mRequestWriteRepairModePassword) {
129             flags |= LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
130         }
131         if (flags == 0) {
132             return Pair.create(true, null);
133         }
134 
135         Intent result = new Intent();
136         final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential,
137                 userId, flags);
138         if (response.isMatched()) {
139             if (mRequestGatekeeperPassword && response.containsGatekeeperPasswordHandle()) {
140                 result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
141                         response.getGatekeeperPasswordHandle());
142             } else if (mRequestGatekeeperPassword) {
143                 Log.e(TAG, "critical: missing GK PW handle for known good credential: " + response);
144             }
145         } else {
146             Log.e(TAG, "critical: bad response for known good credential: " + response);
147         }
148         if (mRequestWriteRepairModePassword) {
149             // Notify the caller if repair mode credential is saved successfully
150             result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL,
151                     response.isMatched());
152         }
153 
154         return Pair.create(true, result);
155     }
156 
finish(Intent resultData)157     private void finish(Intent resultData) {
158         mFinished = true;
159         mResultData = resultData;
160         if (mListener != null) {
161             mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
162         }
163         if (mUnificationProfileCredential != null) {
164             mUnificationProfileCredential.zeroize();
165         }
166         LockScreenSafetySource.onLockScreenChange(getContext());
167     }
168 
setRequestGatekeeperPasswordHandle(boolean value)169     public SaveAndFinishWorker setRequestGatekeeperPasswordHandle(boolean value) {
170         mRequestGatekeeperPassword = value;
171         return this;
172     }
173 
setRequestWriteRepairModePassword(boolean value)174     public SaveAndFinishWorker setRequestWriteRepairModePassword(boolean value) {
175         mRequestWriteRepairModePassword = value;
176         return this;
177     }
178 
setBlocking(boolean blocking)179     public SaveAndFinishWorker setBlocking(boolean blocking) {
180         mBlocking = blocking;
181         return this;
182     }
183 
setProfileToUnify( int profileId, LockscreenCredential credential)184     public SaveAndFinishWorker setProfileToUnify(
185             int profileId, LockscreenCredential credential) {
186         mUnificationProfileId = profileId;
187         mUnificationProfileCredential = credential.duplicate();
188         return this;
189     }
190 
unifyProfileCredentialIfRequested()191     private void unifyProfileCredentialIfRequested() {
192         if (mUnificationProfileId != UserHandle.USER_NULL) {
193             mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
194                     mUnificationProfileCredential);
195         }
196     }
197 
198     private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
199 
200         @Override
doInBackground(Void... params)201         protected Pair<Boolean, Intent> doInBackground(Void... params){
202             return saveAndVerifyInBackground();
203         }
204 
205         @Override
onPostExecute(Pair<Boolean, Intent> resultData)206         protected void onPostExecute(Pair<Boolean, Intent> resultData) {
207             if (!resultData.first) {
208                 Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
209                         Toast.LENGTH_LONG).show();
210             }
211             finish(resultData.second);
212         }
213     }
214 
215     interface Listener {
onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)216         void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
217     }
218 }
219