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