• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.privatespace.onelock;
18 
19 import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS;
20 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE;
21 import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
22 import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
23 import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST;
24 import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST;
25 
26 import android.app.Activity;
27 import android.app.AlertDialog;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.util.Log;
35 
36 import androidx.annotation.Nullable;
37 import androidx.preference.Preference;
38 import androidx.preference.PreferenceScreen;
39 
40 import com.android.internal.widget.LockPatternUtils;
41 import com.android.internal.widget.LockscreenCredential;
42 import com.android.settings.R;
43 import com.android.settings.SettingsPreferenceFragment;
44 import com.android.settings.Utils;
45 import com.android.settings.core.SubSettingLauncher;
46 import com.android.settings.overlay.FeatureFactory;
47 import com.android.settings.password.ChooseLockGeneric;
48 import com.android.settings.password.ChooseLockSettingsHelper;
49 import com.android.settings.privatespace.PrivateProfileContextHelperActivity;
50 import com.android.settings.privatespace.PrivateSpaceMaintainer;
51 import com.android.settingslib.core.AbstractPreferenceController;
52 import com.android.settingslib.transition.SettingsTransitionHelper;
53 import com.android.settingslib.widget.MainSwitchPreference;
54 
55 /** Represents the preference controller for using the same lock as the screen lock */
56 public class UseOneLockControllerSwitch extends AbstractPreferenceController
57         implements Preference.OnPreferenceChangeListener {
58     private static final String TAG = "UseOneLockSwitch";
59     private static final String KEY_UNIFICATION = "private_lock_unification";
60     private final String mPreferenceKey;
61     private final SettingsPreferenceFragment mHost;
62     private final LockPatternUtils mLockPatternUtils;
63     private final UserManager mUserManager;
64     private final int mProfileUserId;
65     private final UserHandle mUserHandle;
66     private LockscreenCredential mCurrentDevicePassword;
67     private LockscreenCredential mCurrentProfilePassword;
68     private MainSwitchPreference mUnifyProfile;
69 
70     @Override
displayPreference(PreferenceScreen screen)71     public void displayPreference(PreferenceScreen screen) {
72         super.displayPreference(screen);
73         mUnifyProfile = screen.findPreference(mPreferenceKey);
74     }
UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host)75     public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host) {
76         this(context, host, KEY_UNIFICATION);
77     }
78 
UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host, String key)79     public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host,
80               String key) {
81         super(context);
82         mHost = host;
83         mUserManager = context.getSystemService(UserManager.class);
84         mLockPatternUtils = FeatureFactory.getFeatureFactory().getSecurityFeatureProvider()
85                   .getLockPatternUtils(context);
86         mUserHandle =  PrivateSpaceMaintainer.getInstance(context).getPrivateProfileHandle();
87         mProfileUserId = mUserHandle != null ? mUserHandle.getIdentifier() : -1;
88         mCurrentDevicePassword = LockscreenCredential.createNone();
89         mCurrentProfilePassword = LockscreenCredential.createNone();
90         this.mPreferenceKey = key;
91     }
92 
93     @Override
getPreferenceKey()94     public String getPreferenceKey() {
95         return mPreferenceKey;
96     }
97 
98     @Override
isAvailable()99     public boolean isAvailable() {
100         return android.os.Flags.allowPrivateProfile()
101                 && android.multiuser.Flags.enablePrivateSpaceFeatures();
102     }
103 
104     @Override
onPreferenceChange(Preference preference, Object value)105     public boolean onPreferenceChange(Preference preference, Object value) {
106         //Checks if the profile is in quiet mode and show a dialog to unpause the profile.
107         if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager, mProfileUserId)) {
108             return false;
109         }
110         final boolean useOneLock = (Boolean) value;
111         if (useOneLock) {
112             startUnification();
113         } else {
114             showAlertDialog();
115         }
116         return true;
117     }
118 
119     @Override
updateState(Preference preference)120     public void updateState(Preference preference) {
121         if (mUnifyProfile != null) {
122             final boolean separate =
123                       mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId);
124             mUnifyProfile.setChecked(!separate);
125         }
126     }
127 
128     /** Method to handle onActivityResult */
handleActivityResult(int requestCode, int resultCode, @Nullable Intent data)129     public boolean handleActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
130         if (requestCode == UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST
131                   && resultCode == Activity.RESULT_OK && data != null) {
132             mCurrentDevicePassword =
133                       data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
134             separateLocks();
135             return true;
136         } else if (requestCode == UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST
137                   && resultCode == Activity.RESULT_OK && data != null) {
138             mCurrentProfilePassword =
139                       data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
140             unifyLocks();
141             return true;
142         }
143         return false;
144     }
145 
separateLocks()146     private void separateLocks() {
147         final Bundle extras = new Bundle();
148         extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
149         extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword);
150         new SubSettingLauncher(mContext)
151                   .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
152                   .setSourceMetricsCategory(mHost.getMetricsCategory())
153                   .setArguments(extras)
154                   .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
155                   .launch();
156     }
157 
158     /** Unify primary and profile locks. */
startUnification()159     public void startUnification() {
160         // Confirm profile lock
161         final ChooseLockSettingsHelper.Builder builder =
162                   new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost);
163         final boolean launched = builder.setRequestCode(UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST)
164                   .setReturnCredentials(true)
165                   .setUserId(mProfileUserId)
166                   .show();
167         if (!launched) {
168             // If profile has no lock, go straight to unification.
169             unifyLocks();
170         }
171     }
172 
unifyLocks()173     private void unifyLocks() {
174         unifyKeepingDeviceLock();
175         if (mCurrentDevicePassword != null) {
176             mCurrentDevicePassword.zeroize();
177             mCurrentDevicePassword = null;
178         }
179         if (mCurrentProfilePassword != null) {
180             mCurrentProfilePassword.zeroize();
181             mCurrentProfilePassword = null;
182         }
183     }
184 
unifyKeepingDeviceLock()185     private void unifyKeepingDeviceLock() {
186         mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false,
187                   mCurrentProfilePassword);
188     }
189 
showAlertDialog()190     private void showAlertDialog() {
191         if (mUserHandle == null) {
192             Log.e(TAG, "Private profile user handle is not expected to be null");
193             mUnifyProfile.setChecked(true);
194             return;
195         }
196         new AlertDialog.Builder(mContext)
197                   .setTitle(R.string.private_space_new_lock_title)
198                   .setMessage(R.string.private_space_new_lock_message)
199                   .setPositiveButton(
200                             R.string.private_space_set_lock_label,
201                             (dialog, which) -> {
202                                 startSeparateLockSetup();
203                             })
204                   .setNegativeButton(R.string.private_space_cancel_label,
205                             (DialogInterface dialog, int which) -> {
206                                 mUnifyProfile.setChecked(true);
207                                 dialog.dismiss();
208                             })
209                   .setOnCancelListener(
210                             (DialogInterface dialog) -> {
211                                 mUnifyProfile.setChecked(true);
212                                 dialog.dismiss();
213                             })
214                   .show();
215     }
216 
startSeparateLockSetup()217     private void startSeparateLockSetup() {
218         if (android.multiuser.Flags.modifyPrivateSpaceSecondaryUnlockSetupFlow()) {
219             final Bundle extras = new Bundle();
220             extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
221             extras.putBoolean(HIDE_INSECURE_OPTIONS, true);
222             extras.putInt(EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE,
223                     R.string.private_space_lock_setup_title);
224             new SubSettingLauncher(mContext).setDestination(ChooseLockGeneric
225                             .ChooseLockGenericFragment.class.getName())
226                     .setSourceMetricsCategory(mHost.getMetricsCategory())
227                     .setArguments(extras)
228                     .setExtras(extras)
229                     .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
230                     .launch();
231         } else {
232             Intent intent = new Intent(mContext, PrivateProfileContextHelperActivity.class);
233             intent.putExtra(EXTRA_ACTION_TYPE, SET_LOCK_ACTION);
234             ((Activity) mContext).startActivityForResultAsUser(intent,
235                     UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST, /*Options*/ null, mUserHandle);
236         }
237     }
238 }
239