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