• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ActivityManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ResolveInfo;
26 import android.os.Build;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.text.BidiFormatter;
30 import android.text.TextUtils;
31 import android.widget.Toast;
32 
33 import androidx.annotation.VisibleForTesting;
34 import androidx.preference.Preference;
35 
36 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
37 import com.android.settings.R;
38 import com.android.settings.Utils;
39 import com.android.settings.core.BasePreferenceController;
40 import com.android.settings.core.InstrumentedPreferenceFragment;
41 import com.android.settings.overlay.FeatureFactory;
42 import com.android.settings.password.ChooseLockSettingsHelper;
43 import com.android.settingslib.RestrictedLockUtils;
44 import com.android.settingslib.RestrictedLockUtilsInternal;
45 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
46 import com.android.settingslib.core.lifecycle.LifecycleObserver;
47 import com.android.settingslib.core.lifecycle.events.OnStart;
48 import com.android.settingslib.development.DevelopmentSettingsEnabler;
49 
50 import com.google.android.setupcompat.util.WizardManagerHelper;
51 
52 public class BuildNumberPreferenceController extends BasePreferenceController implements
53         LifecycleObserver, OnStart {
54 
55     static final int TAPS_TO_BE_A_DEVELOPER = 7;
56     static final int REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF = 100;
57 
58     private Activity mActivity;
59     private InstrumentedPreferenceFragment mFragment;
60     private final UserManager mUm;
61     private final MetricsFeatureProvider mMetricsFeatureProvider;
62 
63     private Toast mDevHitToast;
64     private RestrictedLockUtils.EnforcedAdmin mDebuggingFeaturesDisallowedAdmin;
65     private boolean mDebuggingFeaturesDisallowedBySystem;
66     private int mDevHitCountdown;
67     private boolean mProcessingLastDevHit;
68 
BuildNumberPreferenceController(Context context, String key)69     public BuildNumberPreferenceController(Context context, String key) {
70         super(context, key);
71         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
72         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
73     }
74 
setHost(InstrumentedPreferenceFragment fragment)75     public void setHost(InstrumentedPreferenceFragment fragment) {
76         mFragment = fragment;
77         mActivity = fragment.getActivity();
78     }
79 
80     @Override
getSummary()81     public CharSequence getSummary() {
82         return BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY);
83     }
84 
85     @Override
onStart()86     public void onStart() {
87         mDebuggingFeaturesDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
88                 mContext, UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
89         mDebuggingFeaturesDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
90                 mContext, UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
91         mDevHitCountdown = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
92                 ? -1 : TAPS_TO_BE_A_DEVELOPER;
93         mDevHitToast = null;
94     }
95 
96     @Override
getAvailabilityStatus()97     public int getAvailabilityStatus() {
98         return AVAILABLE;
99     }
100 
101     @Override
useDynamicSliceSummary()102     public boolean useDynamicSliceSummary() {
103         return true;
104     }
105 
106     @Override
handlePreferenceTreeClick(Preference preference)107     public boolean handlePreferenceTreeClick(Preference preference) {
108         if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
109             return false;
110         }
111         if (isUserAMonkey()) {
112             return false;
113         }
114         // Don't enable developer options for secondary non-demo users.
115         if (!(mUm.isAdminUser() || mUm.isDemoUser())) {
116             mMetricsFeatureProvider.action(
117                     mContext, SettingsEnums.ACTION_SETTINGS_BUILD_NUMBER_PREF);
118             return false;
119         }
120 
121         // Don't enable developer options until device has been provisioned
122         if (!WizardManagerHelper.isDeviceProvisioned(mContext)) {
123             mMetricsFeatureProvider.action(
124                     mContext, SettingsEnums.ACTION_SETTINGS_BUILD_NUMBER_PREF);
125             return false;
126         }
127 
128         if (mUm.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
129             if (mUm.isDemoUser()) {
130                 // Route to demo device owner to lift the debugging restriction.
131                 final ComponentName componentName = Utils.getDeviceOwnerComponent(mContext);
132                 if (componentName != null) {
133                     final Intent requestDebugFeatures = new Intent()
134                             .setPackage(componentName.getPackageName())
135                             .setAction("com.android.settings.action.REQUEST_DEBUG_FEATURES");
136                     final ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
137                             requestDebugFeatures, 0);
138                     if (resolveInfo != null) {
139                         mContext.startActivity(requestDebugFeatures);
140                         return false;
141                     }
142                 }
143             }
144             if (mDebuggingFeaturesDisallowedAdmin != null &&
145                     !mDebuggingFeaturesDisallowedBySystem) {
146                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext,
147                         mDebuggingFeaturesDisallowedAdmin);
148             }
149             mMetricsFeatureProvider.action(
150                     mContext, SettingsEnums.ACTION_SETTINGS_BUILD_NUMBER_PREF);
151             return false;
152         }
153 
154         if (mDevHitCountdown > 0) {
155             mDevHitCountdown--;
156             if (mDevHitCountdown == 0 && !mProcessingLastDevHit) {
157                 // Add 1 count back, then start password confirmation flow.
158                 mDevHitCountdown++;
159 
160                 final String title = mContext
161                         .getString(R.string.unlock_set_unlock_launch_picker_title);
162                 final ChooseLockSettingsHelper.Builder builder =
163                         new ChooseLockSettingsHelper.Builder(mActivity, mFragment);
164                 mProcessingLastDevHit = builder
165                         .setRequestCode(REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF)
166                         .setTitle(title)
167                         .show();
168 
169                 if (!mProcessingLastDevHit) {
170                     enableDevelopmentSettings();
171                 }
172                 mMetricsFeatureProvider.action(
173                         mMetricsFeatureProvider.getAttribution(mActivity),
174                         MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
175                         mFragment.getMetricsCategory(),
176                         null,
177                         mProcessingLastDevHit ? 0 : 1);
178             } else if (mDevHitCountdown > 0
179                     && mDevHitCountdown < (TAPS_TO_BE_A_DEVELOPER - 2)) {
180                 if (mDevHitToast != null) {
181                     mDevHitToast.cancel();
182                 }
183                 mDevHitToast = Toast.makeText(mContext,
184                         mContext.getResources().getQuantityString(
185                                 R.plurals.show_dev_countdown, mDevHitCountdown,
186                                 mDevHitCountdown),
187                         Toast.LENGTH_SHORT);
188                 mDevHitToast.show();
189             }
190 
191             mMetricsFeatureProvider.action(
192                     mMetricsFeatureProvider.getAttribution(mActivity),
193                     MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
194                     mFragment.getMetricsCategory(),
195                     null,
196                     0);
197         } else if (mDevHitCountdown < 0) {
198             if (mDevHitToast != null) {
199                 mDevHitToast.cancel();
200             }
201             mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already,
202                     Toast.LENGTH_LONG);
203             mDevHitToast.show();
204             mMetricsFeatureProvider.action(
205                     mMetricsFeatureProvider.getAttribution(mActivity),
206                     MetricsEvent.FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED,
207                     mFragment.getMetricsCategory(),
208                     null,
209                     1);
210         }
211         return true;
212     }
213 
214     /**
215      * Handles password confirmation result.
216      *
217      * @return if activity result is handled.
218      */
onActivityResult(int requestCode, int resultCode, Intent data)219     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
220         if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF) {
221             return false;
222         }
223         if (resultCode == Activity.RESULT_OK) {
224             enableDevelopmentSettings();
225         }
226         mProcessingLastDevHit = false;
227         return true;
228     }
229 
230     /**
231      * Enables development settings. Only call this after confirming password.
232      */
enableDevelopmentSettings()233     private void enableDevelopmentSettings() {
234         mDevHitCountdown = 0;
235         mProcessingLastDevHit = false;
236         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(mContext, true);
237         if (mDevHitToast != null) {
238             mDevHitToast.cancel();
239         }
240         mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on,
241                 Toast.LENGTH_LONG);
242         mDevHitToast.show();
243 
244         FeatureFactory.getFactory(mContext).getSearchFeatureProvider().sendPreIndexIntent(mContext);
245     }
246 
247     @VisibleForTesting
isUserAMonkey()248     protected boolean isUserAMonkey() {
249         return ActivityManager.isUserAMonkey();
250     }
251 }
252