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.car.settings.security;
18 
19 import static android.app.Activity.RESULT_OK;
20 
21 import android.car.drivingstate.CarUxRestrictions;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.UserManager;
27 
28 import androidx.annotation.Nullable;
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.Preference;
31 
32 import com.android.car.settings.R;
33 import com.android.car.settings.common.ActivityResultCallback;
34 import com.android.car.settings.common.FragmentController;
35 import com.android.car.settings.common.Logger;
36 import com.android.car.settings.common.PreferenceController;
37 import com.android.internal.widget.LockscreenCredential;
38 
39 /**
40  * Business Logic for security lock preferences. It can be extended to change which fragment should
41  * be opened when clicked.
42  */
43 public abstract class LockTypeBasePreferenceController extends PreferenceController<Preference>
44         implements ActivityResultCallback {
45 
46     private static final Logger LOG = new Logger(LockTypeBasePreferenceController.class);
47     @VisibleForTesting
48     static final int CHOOSE_LOCK_REQUEST = 90;
49 
50     @VisibleForTesting
51     UserManager mUserManager;
52     private LockscreenCredential mCurrentPassword;
53     private int mCurrentPasswordQuality;
54     private boolean mHasPendingBack = false;
55 
LockTypeBasePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)56     public LockTypeBasePreferenceController(Context context, String preferenceKey,
57             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
58         super(context, preferenceKey, fragmentController, uxRestrictions);
59         mUserManager = UserManager.get(context);
60     }
61 
62     @Override
getPreferenceType()63     protected Class<Preference> getPreferenceType() {
64         return Preference.class;
65     }
66 
67     /**
68      * Intent specified here will determine which activity is started when the Preference is
69      * clicked. Return null to prevent navigation.
70      */
activityToOpen()71     protected abstract Intent activityToOpen();
72 
73     /**
74      * If the current password quality is one of the values returned by this function, the
75      * controller will identify as having the current lock.
76      */
allowedPasswordQualities()77     protected abstract int[] allowedPasswordQualities();
78 
79 
80     /** Sets the quality of the current password. */
setCurrentPasswordQuality(int currentPasswordQuality)81     public void setCurrentPasswordQuality(int currentPasswordQuality) {
82         mCurrentPasswordQuality = currentPasswordQuality;
83     }
84 
85     /** Gets whether the preference related to this controller is the current lock type. */
isCurrentLock()86     protected boolean isCurrentLock() {
87         for (int allowedQuality : allowedPasswordQualities()) {
88             if (mCurrentPasswordQuality == allowedQuality) {
89                 return true;
90             }
91         }
92         return false;
93     }
94 
95     /**
96      * Sets the current password so it can be provided in the bundle in the fragment. The host
97      * fragment is responsible for zeroizing the credentials in memory. */
setCurrentPassword(LockscreenCredential currentPassword)98     public void setCurrentPassword(LockscreenCredential currentPassword) {
99         mCurrentPassword = currentPassword;
100     }
101 
102     /** Gets the current password. */
getCurrentPassword()103     protected LockscreenCredential getCurrentPassword() {
104         return mCurrentPassword;
105     }
106 
107     /** Gets the current password quality. */
getCurrentPasswordQuality()108     protected int getCurrentPasswordQuality() {
109         return mCurrentPasswordQuality;
110     }
111 
112     @Override
updateState(Preference preference)113     protected void updateState(Preference preference) {
114         preference.setSummary(getSummary());
115     }
116 
117     @Override
handlePreferenceClicked(Preference preference)118     protected boolean handlePreferenceClicked(Preference preference) {
119         Intent intent = activityToOpen();
120         if (intent == null) {
121             return false;
122         }
123         intent.putExtra(PasswordHelper.EXTRA_CURRENT_SCREEN_LOCK, mCurrentPassword);
124         getFragmentController().startActivityForResult(intent, CHOOSE_LOCK_REQUEST, this);
125         return true;
126     }
127 
128     /**
129      * Retrieve and set password and password quality
130      */
131     @Override
onCreateInternal()132     protected void onCreateInternal() {
133         if (getPreference().peekExtras() != null) {
134             setCurrentPassword(getPreference().peekExtras().getParcelable(
135                     PasswordHelper.EXTRA_CURRENT_SCREEN_LOCK));
136             setCurrentPasswordQuality(getPreference().peekExtras().getInt(
137                     PasswordHelper.EXTRA_CURRENT_PASSWORD_QUALITY));
138         }
139     }
140 
141     @Override
onStartInternal()142     protected void onStartInternal() {
143         if (mHasPendingBack) {
144             mHasPendingBack = false;
145 
146             // Post the fragment navigation because FragmentManager may still be executing
147             // transactions during onStart.
148             new Handler(Looper.getMainLooper()).post(() -> getFragmentController().goBack());
149         }
150     }
151 
152     @Override
getAvailabilityStatus()153     public int getAvailabilityStatus() {
154         return mUserManager.isGuestUser() ? DISABLED_FOR_PROFILE : AVAILABLE;
155     }
156 
157     @Override
processActivityResult(int requestCode, int resultCode, @Nullable Intent data)158     public void processActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
159         if (requestCode != CHOOSE_LOCK_REQUEST) {
160             return;
161         }
162         if (resultCode != RESULT_OK) {
163             LOG.d("Lock was not updated. Result code: " + resultCode);
164             return;
165         }
166         if (isStarted()) {
167             getFragmentController().goBack();
168         } else {
169             mHasPendingBack = true;
170         }
171     }
172 
getSummary()173     private CharSequence getSummary() {
174         return isCurrentLock() ? getContext().getString(R.string.current_screen_lock) : "";
175     }
176 }
177