Home | History | Annotate | Download | only in settings
      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      */
     83     public RestrictedSettingsFragment(String restrictionKey) {
     84         mRestrictionKey = restrictionKey;
     85     }
     86 
     87     @Override
     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
    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
    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
    129     public void onPause() {
    130         super.onPause();
    131         getActivity().unregisterReceiver(mScreenOffReceiver);
    132     }
    133 
    134     @Override
    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 
    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      */
    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      */
    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 
    220    protected boolean hasChallengeSucceeded() {
    221        return mChallengeSucceeded;
    222    }
    223 
    224    /**
    225     * Returns true if this restrictions key is locked down.
    226     */
    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     */
    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      */
    251    protected void protectByRestrictions(Preference pref) {
    252        if (pref != null) {
    253            mProtectedByRestictionsPrefs.add(pref);
    254        }
    255    }
    256 
    257    protected void protectByRestrictions(String key) {
    258        Preference pref = findPreference(key);
    259        protectByRestrictions(pref);
    260    }
    261 }
    262