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