Home | History | Annotate | Download | only in deviceinfo
      1 /*
      2  * Copyright (C) 2016 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.deviceinfo;
     18 
     19 import android.app.Activity;
     20 import android.app.Fragment;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.os.Build;
     24 import android.os.UserHandle;
     25 import android.os.UserManager;
     26 import android.support.v7.preference.Preference;
     27 import android.support.v7.preference.PreferenceScreen;
     28 import android.text.BidiFormatter;
     29 import android.text.TextUtils;
     30 import android.util.Pair;
     31 import android.widget.Toast;
     32 
     33 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     34 import com.android.settings.R;
     35 import com.android.settings.Utils;
     36 import com.android.settings.core.PreferenceControllerMixin;
     37 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
     38 import com.android.settings.development.DevelopmentSettings;
     39 import com.android.settings.development.DevelopmentSettingsEnabler;
     40 import com.android.settings.overlay.FeatureFactory;
     41 import com.android.settings.password.ChooseLockSettingsHelper;
     42 import com.android.settingslib.RestrictedLockUtils;
     43 import com.android.settingslib.core.AbstractPreferenceController;
     44 import com.android.settingslib.core.lifecycle.Lifecycle;
     45 import com.android.settingslib.core.lifecycle.LifecycleObserver;
     46 import com.android.settingslib.core.lifecycle.events.OnResume;
     47 
     48 public class BuildNumberPreferenceController extends AbstractPreferenceController implements
     49         PreferenceControllerMixin, LifecycleObserver, OnResume {
     50 
     51     static final int TAPS_TO_BE_A_DEVELOPER = 7;
     52     static final int REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF = 100;
     53 
     54     private static final String KEY_BUILD_NUMBER = "build_number";
     55 
     56     private final Activity mActivity;
     57     private final Fragment mFragment;
     58     private final UserManager mUm;
     59     private final MetricsFeatureProvider mMetricsFeatureProvider;
     60 
     61     private Toast mDevHitToast;
     62     private RestrictedLockUtils.EnforcedAdmin mDebuggingFeaturesDisallowedAdmin;
     63     private boolean mDebuggingFeaturesDisallowedBySystem;
     64     private int mDevHitCountdown;
     65     private boolean mProcessingLastDevHit;
     66 
     67     public BuildNumberPreferenceController(Context context, Activity activity, Fragment fragment,
     68             Lifecycle lifecycle) {
     69         super(context);
     70         mActivity = activity;
     71         mFragment = fragment;
     72         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
     73         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     74         if (lifecycle != null) {
     75             lifecycle.addObserver(this);
     76         }
     77     }
     78 
     79     @Override
     80     public void displayPreference(PreferenceScreen screen) {
     81         super.displayPreference(screen);
     82         final Preference preference = screen.findPreference(KEY_BUILD_NUMBER);
     83         if (preference != null) {
     84             try {
     85                 preference.setSummary(BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY));
     86                 preference.setEnabled(true);
     87             } catch (Exception e) {
     88                 preference.setSummary(R.string.device_info_default);
     89             }
     90         }
     91     }
     92 
     93     @Override
     94     public String getPreferenceKey() {
     95         return KEY_BUILD_NUMBER;
     96     }
     97 
     98     @Override
     99     public boolean isAvailable() {
    100         return true;
    101     }
    102 
    103     @Override
    104     public void onResume() {
    105         mDebuggingFeaturesDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(
    106                 mContext, UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
    107         mDebuggingFeaturesDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(
    108                 mContext, UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
    109         mDevHitCountdown = mContext.getSharedPreferences(DevelopmentSettings.PREF_FILE,
    110                 Context.MODE_PRIVATE).getBoolean(DevelopmentSettings.PREF_SHOW,
    111                 android.os.Build.TYPE.equals("eng")) ? -1 : TAPS_TO_BE_A_DEVELOPER;
    112         mDevHitToast = null;
    113     }
    114 
    115     @Override
    116     public boolean handlePreferenceTreeClick(Preference preference) {
    117         if (!TextUtils.equals(preference.getKey(), KEY_BUILD_NUMBER)) {
    118             return false;
    119         }
    120         if (Utils.isMonkeyRunning()) {
    121             return false;
    122         }
    123         // Don't enable developer options for secondary users.
    124         if (!mUm.isAdminUser()) {
    125             mMetricsFeatureProvider.action(
    126                     mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF);
    127             return false;
    128         }
    129 
    130         // Don't enable developer options until device has been provisioned
    131         if (!Utils.isDeviceProvisioned(mContext)) {
    132             mMetricsFeatureProvider.action(
    133                     mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF);
    134             return false;
    135         }
    136 
    137         if (mUm.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
    138             if (mDebuggingFeaturesDisallowedAdmin != null &&
    139                     !mDebuggingFeaturesDisallowedBySystem) {
    140                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext,
    141                         mDebuggingFeaturesDisallowedAdmin);
    142             }
    143             mMetricsFeatureProvider.action(
    144                     mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF);
    145             return false;
    146         }
    147 
    148         if (mDevHitCountdown > 0) {
    149             mDevHitCountdown--;
    150             if (mDevHitCountdown == 0 && !mProcessingLastDevHit) {
    151                 // Add 1 count back, then start password confirmation flow.
    152                 mDevHitCountdown++;
    153                 final ChooseLockSettingsHelper helper =
    154                         new ChooseLockSettingsHelper(mActivity, mFragment);
    155                 mProcessingLastDevHit = helper.launchConfirmationActivity(
    156                         REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF,
    157                         mContext.getString(R.string.unlock_set_unlock_launch_picker_title));
    158                 if (!mProcessingLastDevHit) {
    159                     enableDevelopmentSettings();
    160                 }
    161                 mMetricsFeatureProvider.action(
    162                         mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF,
    163                         Pair.create(MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
    164                                 mProcessingLastDevHit ? 0 : 1));
    165             } else if (mDevHitCountdown > 0
    166                     && mDevHitCountdown < (TAPS_TO_BE_A_DEVELOPER - 2)) {
    167                 if (mDevHitToast != null) {
    168                     mDevHitToast.cancel();
    169                 }
    170                 mDevHitToast = Toast.makeText(mContext,
    171                         mContext.getResources().getQuantityString(
    172                                 R.plurals.show_dev_countdown, mDevHitCountdown,
    173                                 mDevHitCountdown),
    174                         Toast.LENGTH_SHORT);
    175                 mDevHitToast.show();
    176             }
    177             mMetricsFeatureProvider.action(
    178                     mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF,
    179                     Pair.create(MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
    180                             0));
    181         } else if (mDevHitCountdown < 0) {
    182             if (mDevHitToast != null) {
    183                 mDevHitToast.cancel();
    184             }
    185             mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already,
    186                     Toast.LENGTH_LONG);
    187             mDevHitToast.show();
    188             mMetricsFeatureProvider.action(
    189                     mContext, MetricsEvent.ACTION_SETTINGS_BUILD_NUMBER_PREF,
    190                     Pair.create(MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
    191                             1));
    192         }
    193         return true;
    194     }
    195 
    196     /**
    197      * Handles password confirmation result.
    198      *
    199      * @return if activity result is handled.
    200      */
    201     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
    202         if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF) {
    203             return false;
    204         }
    205         if (resultCode == Activity.RESULT_OK) {
    206             enableDevelopmentSettings();
    207         }
    208         mProcessingLastDevHit = false;
    209         return true;
    210     }
    211 
    212     /**
    213      * Enables development settings. Only call this after confirming password.
    214      */
    215     private void enableDevelopmentSettings() {
    216         mDevHitCountdown = 0;
    217         mProcessingLastDevHit = false;
    218         DevelopmentSettingsEnabler.enableDevelopmentSettings(mContext,
    219                 mContext.getSharedPreferences(DevelopmentSettings.PREF_FILE,
    220                         Context.MODE_PRIVATE));
    221         if (mDevHitToast != null) {
    222             mDevHitToast.cancel();
    223         }
    224         mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on,
    225                 Toast.LENGTH_LONG);
    226         mDevHitToast.show();
    227         // This is good time to index the Developer Options
    228         FeatureFactory.getFactory(mContext).getSearchFeatureProvider().getIndexingManager(mContext)
    229                 .updateFromClassNameResource(DevelopmentSettings.class.getName(),
    230                         true /* includeInSearchResults */);
    231     }
    232 }
    233