1 /* 2 * Copyright (C) 2013 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; 18 19 import java.util.HashSet; 20 21 import android.app.Activity; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.Bundle; 27 import android.os.UserManager; 28 import android.preference.CheckBoxPreference; 29 import android.preference.Preference; 30 31 /** 32 * Base class for settings activities that should be pin protected when in restricted mode. 33 * The constructor for this class will take the restriction key that this screen should be 34 * locked by. If {@link UserManager.hasRestrictionsPin()} and 35 * {@link UserManager.hasUserRestriction(String)} returns true for the restriction key, then 36 * the user will have to enter the restrictions pin before seeing the Settings screen. 37 * 38 * If this settings screen should be pin protected whenever 39 * {@link UserManager.hasUserRestriction(String)} returns true, pass in 40 * {@link RESTRICTIONS_PIN_SET} to the constructor instead of a restrictions key. 41 */ 42 public class RestrictedSettingsFragment extends SettingsPreferenceFragment { 43 44 protected static final String RESTRICTIONS_PIN_SET = "restrictions_pin_set"; 45 46 private static final String EXTRA_PREFERENCE = "pref"; 47 private static final String EXTRA_CHECKBOX_STATE = "isChecked"; 48 // Should be unique across all settings screens that use this. 49 private static final int REQUEST_PIN_CHALLENGE = 12309; 50 51 private static final String KEY_CHALLENGE_SUCCEEDED = "chsc"; 52 private static final String KEY_CHALLENGE_REQUESTED = "chrq"; 53 private static final String KEY_RESUME_ACTION_BUNDLE = "rsmb"; 54 55 // If the restriction PIN is entered correctly. 56 private boolean mChallengeSucceeded; 57 private boolean mChallengeRequested; 58 private Bundle mResumeActionBundle; 59 60 private UserManager mUserManager; 61 62 private final String mRestrictionKey; 63 64 private final HashSet<Preference> mProtectedByRestictionsPrefs = new HashSet<Preference>(); 65 66 // Receiver to clear pin status when the screen is turned off. 67 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 68 @Override 69 public void onReceive(Context context, Intent intent) { 70 mChallengeSucceeded = false; 71 if (shouldBePinProtected(mRestrictionKey)) { 72 ensurePin(null); 73 } 74 } 75 }; 76 77 /** 78 * @param restrictionKey The restriction key to check before pin protecting 79 * this settings page. Pass in {@link RESTRICTIONS_PIN_SET} if it should 80 * be PIN protected whenever a restrictions pin is set. Pass in 81 * null if it should never be PIN protected. 82 */ RestrictedSettingsFragment(String restrictionKey)83 public RestrictedSettingsFragment(String restrictionKey) { 84 mRestrictionKey = restrictionKey; 85 } 86 87 @Override onCreate(Bundle icicle)88 public void onCreate(Bundle icicle) { 89 super.onCreate(icicle); 90 91 mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); 92 93 if (icicle != null) { 94 mChallengeSucceeded = icicle.getBoolean(KEY_CHALLENGE_SUCCEEDED, false); 95 mChallengeRequested = icicle.getBoolean(KEY_CHALLENGE_REQUESTED, false); 96 mResumeActionBundle = icicle.getBundle(KEY_RESUME_ACTION_BUNDLE); 97 } 98 } 99 100 @Override onSaveInstanceState(Bundle outState)101 public void onSaveInstanceState(Bundle outState) { 102 super.onSaveInstanceState(outState); 103 104 outState.putBoolean(KEY_CHALLENGE_REQUESTED, mChallengeRequested); 105 if (mResumeActionBundle != null) { 106 outState.putBundle(KEY_RESUME_ACTION_BUNDLE, mResumeActionBundle); 107 } 108 if (getActivity().isChangingConfigurations()) { 109 outState.putBoolean(KEY_CHALLENGE_SUCCEEDED, mChallengeSucceeded); 110 } 111 } 112 113 @Override onResume()114 public void onResume() { 115 super.onResume(); 116 if (shouldBePinProtected(mRestrictionKey)) { 117 ensurePin(null); 118 } else { 119 // If the whole screen is not pin protected, reset mChallengeSucceeded so next 120 // time user uses a protected preference, they are prompted for pin again. 121 mChallengeSucceeded = false; 122 } 123 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 124 filter.addAction(Intent.ACTION_USER_PRESENT); 125 getActivity().registerReceiver(mScreenOffReceiver, filter); 126 } 127 128 @Override onPause()129 public void onPause() { 130 super.onPause(); 131 getActivity().unregisterReceiver(mScreenOffReceiver); 132 } 133 134 @Override onActivityResult(int requestCode, int resultCode, Intent data)135 public void onActivityResult(int requestCode, int resultCode, Intent data) { 136 if (requestCode == REQUEST_PIN_CHALLENGE) { 137 Bundle resumeActionBundle = mResumeActionBundle; 138 mResumeActionBundle = null; 139 mChallengeRequested = false; 140 if (resultCode == Activity.RESULT_OK) { 141 mChallengeSucceeded = true; 142 String prefKey = resumeActionBundle == null ? 143 null : resumeActionBundle.getString(EXTRA_PREFERENCE); 144 if (prefKey != null) { 145 Preference pref = findPreference(prefKey); 146 if (pref != null) { 147 // Make sure the checkbox state is the same as it was when we launched the 148 // pin challenge. 149 if (pref instanceof CheckBoxPreference 150 && resumeActionBundle.containsKey(EXTRA_CHECKBOX_STATE)) { 151 boolean isChecked = 152 resumeActionBundle.getBoolean(EXTRA_CHECKBOX_STATE, false); 153 ((CheckBoxPreference)pref).setChecked(isChecked); 154 } 155 if (!onPreferenceTreeClick(getPreferenceScreen(), pref)) { 156 Intent prefIntent = pref.getIntent(); 157 if (prefIntent != null) { 158 pref.getContext().startActivity(prefIntent); 159 } 160 } 161 } 162 } 163 } else if (!isDetached()) { 164 finishFragment(); 165 } 166 return; 167 } 168 169 super.onActivityResult(requestCode, resultCode, data); 170 } 171 ensurePin(Preference preference)172 private void ensurePin(Preference preference) { 173 if (!mChallengeSucceeded) { 174 final UserManager um = UserManager.get(getActivity()); 175 if (!mChallengeRequested) { 176 if (um.hasRestrictionsChallenge()) { 177 mResumeActionBundle = new Bundle(); 178 if (preference != null) { 179 mResumeActionBundle.putString(EXTRA_PREFERENCE, preference.getKey()); 180 if (preference instanceof CheckBoxPreference) { 181 mResumeActionBundle.putBoolean(EXTRA_CHECKBOX_STATE, 182 ((CheckBoxPreference)preference).isChecked()); 183 } 184 } 185 Intent requestPin = new Intent(Intent.ACTION_RESTRICTIONS_CHALLENGE); 186 startActivityForResult(requestPin, REQUEST_PIN_CHALLENGE); 187 mChallengeRequested = true; 188 } 189 } 190 } 191 mChallengeSucceeded = false; 192 } 193 194 /** 195 * Returns true if this activity is restricted, but no restriction pin has been set. 196 * Used to determine if the settings UI should disable UI. 197 */ isRestrictedAndNotPinProtected()198 protected boolean isRestrictedAndNotPinProtected() { 199 if (mRestrictionKey == null || RESTRICTIONS_PIN_SET.equals(mRestrictionKey)) { 200 return false; 201 } 202 return mUserManager.hasUserRestriction(mRestrictionKey) 203 && !mUserManager.hasRestrictionsChallenge(); 204 } 205 206 /** 207 * Called to trigger the pin entry if the given restriction key is locked down. 208 * @param restrictionsKey The restriction key or {@link RESTRICTIONS_PIN_SET} if 209 * pin entry should get triggered if there is a pin set. 210 */ restrictionsPinCheck(String restrictionsKey, Preference preference)211 protected boolean restrictionsPinCheck(String restrictionsKey, Preference preference) { 212 if (shouldBePinProtected(restrictionsKey) && !mChallengeSucceeded) { 213 ensurePin(preference); 214 return false; 215 } else { 216 return true; 217 } 218 } 219 hasChallengeSucceeded()220 protected boolean hasChallengeSucceeded() { 221 return mChallengeSucceeded; 222 } 223 224 /** 225 * Returns true if this restrictions key is locked down. 226 */ shouldBePinProtected(String restrictionKey)227 protected boolean shouldBePinProtected(String restrictionKey) { 228 if (restrictionKey == null) { 229 return false; 230 } 231 boolean restricted = RESTRICTIONS_PIN_SET.equals(restrictionKey) 232 || mUserManager.hasUserRestriction(restrictionKey); 233 return restricted && mUserManager.hasRestrictionsChallenge(); 234 } 235 236 /** 237 * If the preference is one that was added by protectByRestrictions(), then it will 238 * prompt the user for the restrictions pin if they haven't entered it already. 239 * Intended to be called at the top of onPreferenceTreeClick. If this function returns 240 * true, then onPreferenceTreeClick should return true. 241 */ ensurePinRestrictedPreference(Preference preference)242 boolean ensurePinRestrictedPreference(Preference preference) { 243 return mProtectedByRestictionsPrefs.contains(preference) 244 && !restrictionsPinCheck(RESTRICTIONS_PIN_SET, preference); 245 } 246 247 /** 248 * Call this with any preferences that should require the PIN to be entered 249 * before they are accessible. 250 */ protectByRestrictions(Preference pref)251 protected void protectByRestrictions(Preference pref) { 252 if (pref != null) { 253 mProtectedByRestictionsPrefs.add(pref); 254 } 255 } 256 protectByRestrictions(String key)257 protected void protectByRestrictions(String key) { 258 Preference pref = findPreference(key); 259 protectByRestrictions(pref); 260 } 261 } 262