• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.development;
18 
19 import static android.app.Activity.RESULT_OK;
20 import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
21 import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
22 import static android.view.flags.Flags.sensitiveContentAppProtectionApi;
23 
24 import android.app.Activity;
25 import android.app.settings.SettingsEnums;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothCodecStatus;
29 import android.bluetooth.BluetoothProfile;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.database.ContentObserver;
37 import android.net.Uri;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.SystemProperties;
42 import android.os.UserManager;
43 import android.provider.Settings;
44 import android.text.TextUtils;
45 import android.util.Log;
46 import android.view.LayoutInflater;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.widget.CompoundButton;
50 import android.widget.CompoundButton.OnCheckedChangeListener;
51 import android.widget.Toast;
52 
53 import androidx.annotation.Nullable;
54 import androidx.annotation.VisibleForTesting;
55 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
56 
57 import com.android.settings.R;
58 import com.android.settings.SettingsActivity;
59 import com.android.settings.Utils;
60 import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog;
61 import com.android.settings.core.SubSettingLauncher;
62 import com.android.settings.dashboard.RestrictedDashboardFragment;
63 import com.android.settings.development.autofill.AutofillCategoryController;
64 import com.android.settings.development.autofill.AutofillLoggingLevelPreferenceController;
65 import com.android.settings.development.autofill.AutofillResetOptionsPreferenceController;
66 import com.android.settings.development.bluetooth.AbstractBluetoothDialogPreferenceController;
67 import com.android.settings.development.bluetooth.AbstractBluetoothPreferenceController;
68 import com.android.settings.development.bluetooth.BluetoothBitPerSampleDialogPreferenceController;
69 import com.android.settings.development.bluetooth.BluetoothChannelModeDialogPreferenceController;
70 import com.android.settings.development.bluetooth.BluetoothCodecListPreferenceController;
71 import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController;
72 import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController;
73 import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
74 import com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController;
75 import com.android.settings.development.desktopexperience.DesktopExperiencePreferenceController;
76 import com.android.settings.development.desktopexperience.DesktopModePreferenceController;
77 import com.android.settings.development.desktopexperience.DesktopModeSecondaryDisplayPreferenceController;
78 import com.android.settings.development.desktopexperience.FreeformWindowsPreferenceController;
79 import com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController;
80 import com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController;
81 import com.android.settings.development.qstile.DevelopmentTiles;
82 import com.android.settings.development.storage.SharedDataPreferenceController;
83 import com.android.settings.development.window.NonResizableMultiWindowPreferenceController;
84 import com.android.settings.development.window.ResizableActivityPreferenceController;
85 import com.android.settings.overlay.FeatureFactory;
86 import com.android.settings.password.ConfirmDeviceCredentialActivity;
87 import com.android.settings.search.BaseSearchIndexProvider;
88 import com.android.settings.search.actionbar.SearchMenuController;
89 import com.android.settings.widget.SettingsMainSwitchBar;
90 import com.android.settingslib.core.AbstractPreferenceController;
91 import com.android.settingslib.core.lifecycle.Lifecycle;
92 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
93 import com.android.settingslib.development.DevelopmentSettingsEnabler;
94 import com.android.settingslib.development.SystemPropPoker;
95 import com.android.settingslib.search.SearchIndexable;
96 
97 import com.google.android.setupcompat.util.WizardManagerHelper;
98 
99 import java.util.ArrayList;
100 import java.util.List;
101 
102 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
103 public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
104         implements OnCheckedChangeListener, OemUnlockDialogHost, AdbDialogHost,
105         AdbClearKeysDialogHost, LogPersistDialogHost,
106         BluetoothRebootDialog.OnRebootDialogListener,
107         AbstractBluetoothPreferenceController.Callback,
108         NfcRebootDialog.OnNfcRebootDialogConfirmedListener,
109         BluetoothSnoopLogHost,
110         BluetoothLeAudioModePreferenceController.OnModeChangeListener {
111 
112     private static final String TAG = "DevSettingsDashboard";
113     @VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100;
114 
115     private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore =
116             new BluetoothA2dpConfigStore();
117 
118     private boolean mIsAvailable = true;
119     private boolean mIsBiometricsAuthenticated;
120     private SettingsMainSwitchBar mSwitchBar;
121     private DevelopmentSwitchBarController mSwitchBarController;
122     private List<AbstractPreferenceController> mPreferenceControllers = new ArrayList<>();
123     private BluetoothA2dp mBluetoothA2dp;
124 
125     private final BroadcastReceiver mEnableAdbReceiver = new BroadcastReceiver() {
126         @Override
127         public void onReceive(Context context, Intent intent) {
128             for (AbstractPreferenceController controller : mPreferenceControllers) {
129                 if (controller instanceof AdbOnChangeListener) {
130                     ((AdbOnChangeListener) controller).onAdbSettingChanged();
131                 }
132             }
133         }
134     };
135 
136     private final BroadcastReceiver mBluetoothA2dpReceiver = new BroadcastReceiver() {
137         @Override
138         public void onReceive(Context context, Intent intent) {
139             Log.d(TAG, "mBluetoothA2dpReceiver.onReceive intent=" + intent);
140             String action = intent.getAction();
141 
142             if (BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED.equals(action)) {
143                 BluetoothCodecStatus codecStatus = intent.getParcelableExtra(
144                         BluetoothCodecStatus.EXTRA_CODEC_STATUS);
145                 Log.d(TAG, "Received BluetoothCodecStatus=" + codecStatus);
146                 for (AbstractPreferenceController controller : mPreferenceControllers) {
147                     if (controller instanceof BluetoothServiceConnectionListener) {
148                         ((BluetoothServiceConnectionListener) controller).onBluetoothCodecUpdated();
149                     }
150                 }
151             }
152         }
153     };
154 
155 
156     private final BluetoothProfile.ServiceListener mBluetoothA2dpServiceListener =
157             new BluetoothProfile.ServiceListener() {
158                 @Override
159                 public void onServiceConnected(int profile,
160                         BluetoothProfile proxy) {
161                     synchronized (mBluetoothA2dpConfigStore) {
162                         mBluetoothA2dp = (BluetoothA2dp) proxy;
163                     }
164                     for (AbstractPreferenceController controller : mPreferenceControllers) {
165                         if (controller instanceof BluetoothServiceConnectionListener) {
166                             ((BluetoothServiceConnectionListener) controller)
167                                     .onBluetoothServiceConnected(mBluetoothA2dp);
168                         }
169                     }
170                 }
171 
172                 @Override
173                 public void onServiceDisconnected(int profile) {
174                     synchronized (mBluetoothA2dpConfigStore) {
175                         mBluetoothA2dp = null;
176                     }
177                     for (AbstractPreferenceController controller : mPreferenceControllers) {
178                         if (controller instanceof BluetoothServiceConnectionListener) {
179                             ((BluetoothServiceConnectionListener) controller)
180                                     .onBluetoothServiceDisconnected();
181                         }
182                     }
183                 }
184             };
185 
186     private final Runnable mSystemPropertiesChanged = new Runnable() {
187         @Override
188         public void run() {
189             synchronized (this) {
190                 Activity activity = getActivity();
191                 if (activity != null) {
192                     activity.runOnUiThread(() -> {
193                         updatePreferenceStates();
194                     });
195                 }
196             }
197         }
198     };
199 
200     private final Uri mDevelopEnabled = Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED);
201     private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(new Handler(
202             Looper.getMainLooper())) {
203 
204         @Override
205         public void onChange(boolean selfChange, Uri uri) {
206             super.onChange(selfChange, uri);
207             Activity activity = getActivity();
208             if (activity == null) {
209                 return;
210             }
211             final boolean developmentEnabledState =
212                     DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(activity);
213             final boolean switchState = mSwitchBar.isChecked();
214 
215             // when developer options is enabled, but it is disabled by other privilege apps like:
216             // adb command, we should disable all items and finish the activity.
217             if (developmentEnabledState != switchState) {
218                 if (developmentEnabledState) {
219                     return;
220                 }
221                 disableDeveloperOptions();
222                 activity.runOnUiThread(() -> finishFragment());
223             }
224         }
225     };
226 
DevelopmentSettingsDashboardFragment()227     public DevelopmentSettingsDashboardFragment() {
228         super(UserManager.DISALLOW_DEBUGGING_FEATURES);
229     }
230 
231     @Override
onStart()232     public void onStart() {
233         super.onStart();
234         final ContentResolver cr = getContext().getContentResolver();
235         mIsBiometricsAuthenticated = false;
236         cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver);
237 
238         // Restore UI state based on whether developer options is enabled
239         if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
240             enableDeveloperOptions();
241             handleQsTileLongPressActionIfAny();
242         } else {
243             disableDeveloperOptions();
244         }
245     }
246 
247     @Override
onStop()248     public void onStop() {
249         super.onStop();
250         final ContentResolver cr = getContext().getContentResolver();
251         cr.unregisterContentObserver(mDeveloperSettingsObserver);
252     }
253 
254     @Override
onCreate(Bundle icicle)255     public void onCreate(Bundle icicle) {
256         super.onCreate(icicle);
257         SearchMenuController.init(this);
258         if (Utils.isMonkeyRunning()) {
259             getActivity().finish();
260             return;
261         }
262         Context context = requireContext();
263         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
264 
265         if (!um.isAdminUser()) {
266             Toast.makeText(context, R.string.dev_settings_available_to_admin_only_warning,
267                             Toast.LENGTH_SHORT)
268                     .show();
269             finish();
270         } else if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)) {
271             Toast.makeText(context, R.string.dev_settings_disabled_warning, Toast.LENGTH_SHORT)
272                     .show();
273             finish();
274         }
275     }
276 
277     @Override
onActivityCreated(Bundle icicle)278     public void onActivityCreated(Bundle icicle) {
279         super.onActivityCreated(icicle);
280         // Apply page-level restrictions
281         setIfOnlyAvailableForAdmins(true);
282         if (isUiRestricted() || !WizardManagerHelper.isDeviceProvisioned(getActivity())) {
283             // Block access to developer options if the user is not the owner, if user policy
284             // restricts it, or if the device has not been provisioned
285             mIsAvailable = false;
286             // Show error message
287             if (!isUiRestrictedByOnlyAdmin()) {
288                 getEmptyTextView().setText(
289                         com.android.settingslib.R.string.development_settings_not_available);
290             }
291             getPreferenceScreen().removeAll();
292             return;
293         }
294         // Set up primary switch
295         mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
296         mSwitchBar.setTitle(getContext().getString(R.string.developer_options_main_switch_title));
297         mSwitchBar.show();
298         mSwitchBarController = new DevelopmentSwitchBarController(
299                 this /* DevelopmentSettings */, mSwitchBar, mIsAvailable,
300                 getSettingsLifecycle());
301     }
302 
303     @Override
shouldSkipForInitialSUW()304     protected boolean shouldSkipForInitialSUW() {
305         return true;
306     }
307 
308     /**
309      * Long-pressing a developer options quick settings tile will by default (see
310      * QS_TILE_PREFERENCES in the manifest) take you to the developer options page.
311      * Some tiles may want to go into their own page within the developer options.
312      */
handleQsTileLongPressActionIfAny()313     private void handleQsTileLongPressActionIfAny() {
314         Intent intent = getActivity().getIntent();
315         if (intent == null || !TextUtils.equals(ACTION_QS_TILE_PREFERENCES, intent.getAction())) {
316             return;
317         }
318 
319         Log.d(TAG, "Developer options started from qstile long-press");
320         final ComponentName componentName = (ComponentName) intent.getParcelableExtra(
321                 Intent.EXTRA_COMPONENT_NAME);
322         if (componentName == null) {
323             return;
324         }
325 
326         if (DevelopmentTiles.WirelessDebugging.class.getName().equals(
327                 componentName.getClassName()) && getDevelopmentOptionsController(
328                 WirelessDebuggingPreferenceController.class).isAvailable()) {
329             Log.d(TAG, "Long press from wireless debugging qstile");
330             new SubSettingLauncher(getContext())
331                     .setDestination(WirelessDebuggingFragment.class.getName())
332                     .setSourceMetricsCategory(SettingsEnums.SETTINGS_ADB_WIRELESS)
333                     .launch();
334         }
335         // Add other qstiles here
336     }
337 
338     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)339     public View onCreateView(LayoutInflater inflater, ViewGroup container,
340             Bundle savedInstanceState) {
341         registerReceivers();
342         SystemProperties.addChangeCallback(mSystemPropertiesChanged);
343         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
344         if (adapter != null) {
345             adapter.getProfileProxy(getActivity(), mBluetoothA2dpServiceListener,
346                     BluetoothProfile.A2DP);
347         }
348         View root = super.onCreateView(inflater, container, savedInstanceState);
349         // Mark the view sensitive to black out the screen during screen share.
350         if (sensitiveContentAppProtectionApi()) {
351             root.setContentSensitivity(View.CONTENT_SENSITIVITY_SENSITIVE);
352         }
353         return root;
354     }
355 
356     @Override
onDestroyView()357     public void onDestroyView() {
358         super.onDestroyView();
359         unregisterReceivers();
360 
361         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
362         if (adapter != null) {
363             adapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
364             mBluetoothA2dp = null;
365         }
366         SystemProperties.removeChangeCallback(mSystemPropertiesChanged);
367     }
368 
369     @Override
getMetricsCategory()370     public int getMetricsCategory() {
371         return SettingsEnums.DEVELOPMENT;
372     }
373 
374     @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)375     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
376         final boolean developmentEnabledState =
377                 DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext());
378         if (isChecked != developmentEnabledState) {
379             if (isChecked) {
380                 final int userId = getContext().getUserId();
381 
382                 final Utils.BiometricStatus biometricAuthStatus =
383                         Utils.requestBiometricAuthenticationForMandatoryBiometrics(
384                                 getContext(),
385                                 mIsBiometricsAuthenticated,
386                                 userId);
387                 if (biometricAuthStatus == Utils.BiometricStatus.OK) {
388                     mSwitchBar.setChecked(false);
389                     Utils.launchBiometricPromptForMandatoryBiometrics(this,
390                             REQUEST_BIOMETRIC_PROMPT,
391                             userId, false /* hideBackground */);
392                 } else if (biometricAuthStatus != Utils.BiometricStatus.NOT_ACTIVE) {
393                     mSwitchBar.setChecked(false);
394                     IdentityCheckBiometricErrorDialog.showBiometricErrorDialog(getActivity(),
395                             biometricAuthStatus, false /* twoFactorAuthentication */);
396                 } else {
397                     //Reset biometrics once enable dialog is shown
398                     mIsBiometricsAuthenticated = false;
399                     EnableDevelopmentSettingWarningDialog.show(this /* host */);
400                 }
401             } else {
402                 final BluetoothA2dpHwOffloadPreferenceController a2dpController =
403                         getDevelopmentOptionsController(
404                                 BluetoothA2dpHwOffloadPreferenceController.class);
405                 final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
406                         getDevelopmentOptionsController(
407                                 BluetoothLeAudioHwOffloadPreferenceController.class);
408                 final NfcSnoopLogPreferenceController nfcSnoopLogController =
409                         getDevelopmentOptionsController(
410                                 NfcSnoopLogPreferenceController.class);
411                 final NfcVerboseVendorLogPreferenceController nfcVerboseLogController =
412                         getDevelopmentOptionsController(
413                                 NfcVerboseVendorLogPreferenceController.class);
414                 final GraphicsDriverEnableAngleAsSystemDriverController enableAngleController =
415                         getDevelopmentOptionsController(
416                                 GraphicsDriverEnableAngleAsSystemDriverController.class);
417                 // If hardware offload isn't default value, we must reboot after disable
418                 // developer options. Show a dialog for the user to confirm.
419                 if ((a2dpController == null || a2dpController.isDefaultValue())
420                         && (leAudioController == null || leAudioController.isDefaultValue())
421                         && (nfcSnoopLogController == null || nfcSnoopLogController.isDefaultValue())
422                         && (nfcVerboseLogController == null
423                         || nfcVerboseLogController.isDefaultValue())
424                         && (enableAngleController == null
425                         || enableAngleController.isDefaultValue())) {
426                     disableDeveloperOptions();
427                 } else {
428                     // Disabling developer options in page-agnostic mode isn't supported as device
429                     // isn't in production state
430                     if (Enable16kUtils.isPageAgnosticModeOn(getContext())) {
431                         Enable16kUtils.showPageAgnosticWarning(getContext());
432                         onDisableDevelopmentOptionsRejected();
433                         return;
434                     }
435                     DisableDevSettingsDialogFragment.show(this /* host */);
436                 }
437             }
438             FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
439                     .sendPreIndexIntent(getContext());
440         }
441     }
442 
443     @Override
onOemUnlockDialogConfirmed()444     public void onOemUnlockDialogConfirmed() {
445         final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
446                 OemUnlockPreferenceController.class);
447         controller.onOemUnlockConfirmed();
448     }
449 
450     @Override
onOemUnlockDialogDismissed()451     public void onOemUnlockDialogDismissed() {
452         final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
453                 OemUnlockPreferenceController.class);
454         controller.onOemUnlockDismissed();
455     }
456 
457     @Override
onEnableAdbDialogConfirmed()458     public void onEnableAdbDialogConfirmed() {
459         final AdbPreferenceController controller = getDevelopmentOptionsController(
460                 AdbPreferenceController.class);
461         controller.onAdbDialogConfirmed();
462 
463     }
464 
465     @Override
onEnableAdbDialogDismissed()466     public void onEnableAdbDialogDismissed() {
467         final AdbPreferenceController controller = getDevelopmentOptionsController(
468                 AdbPreferenceController.class);
469         controller.onAdbDialogDismissed();
470     }
471 
472     @Override
onAdbClearKeysDialogConfirmed()473     public void onAdbClearKeysDialogConfirmed() {
474         final ClearAdbKeysPreferenceController controller = getDevelopmentOptionsController(
475                 ClearAdbKeysPreferenceController.class);
476         controller.onClearAdbKeysConfirmed();
477     }
478 
479     @Override
onDisableLogPersistDialogConfirmed()480     public void onDisableLogPersistDialogConfirmed() {
481         final LogPersistPreferenceController controller = getDevelopmentOptionsController(
482                 LogPersistPreferenceController.class);
483         controller.onDisableLogPersistDialogConfirmed();
484     }
485 
486     @Override
onDisableLogPersistDialogRejected()487     public void onDisableLogPersistDialogRejected() {
488         final LogPersistPreferenceController controller = getDevelopmentOptionsController(
489                 LogPersistPreferenceController.class);
490         controller.onDisableLogPersistDialogRejected();
491     }
492 
493     @Override
onRebootDialogConfirmed()494     public void onRebootDialogConfirmed() {
495         final BluetoothA2dpHwOffloadPreferenceController a2dpController =
496                 getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
497         a2dpController.onRebootDialogConfirmed();
498 
499         final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
500                 getDevelopmentOptionsController(
501                         BluetoothLeAudioHwOffloadPreferenceController.class);
502         leAudioController.onRebootDialogConfirmed();
503 
504         final BluetoothLeAudioPreferenceController leAudioFeatureController =
505                 getDevelopmentOptionsController(
506                         BluetoothLeAudioPreferenceController.class);
507         leAudioFeatureController.onRebootDialogConfirmed();
508 
509         final BluetoothLeAudioModePreferenceController leAudioModeController =
510                 getDevelopmentOptionsController(
511                         BluetoothLeAudioModePreferenceController.class);
512         leAudioModeController.onRebootDialogConfirmed();
513 
514         final BluetoothLeAudioUiPreferenceController leAudioUiController =
515                 getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
516         leAudioUiController.onRebootDialogConfirmed();
517     }
518 
519     @Override
onRebootDialogCanceled()520     public void onRebootDialogCanceled() {
521         final BluetoothA2dpHwOffloadPreferenceController a2dpController =
522                 getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
523         a2dpController.onRebootDialogCanceled();
524 
525         final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
526                 getDevelopmentOptionsController(
527                         BluetoothLeAudioHwOffloadPreferenceController.class);
528         leAudioController.onRebootDialogCanceled();
529 
530         final BluetoothLeAudioPreferenceController leAudioFeatureController =
531                 getDevelopmentOptionsController(
532                         BluetoothLeAudioPreferenceController.class);
533         leAudioFeatureController.onRebootDialogCanceled();
534 
535         final BluetoothLeAudioModePreferenceController leAudioModeController =
536                 getDevelopmentOptionsController(
537                         BluetoothLeAudioModePreferenceController.class);
538         leAudioModeController.onRebootDialogCanceled();
539 
540         final BluetoothLeAudioUiPreferenceController leAudioUiController =
541                 getDevelopmentOptionsController(BluetoothLeAudioUiPreferenceController.class);
542         leAudioUiController.onRebootDialogCanceled();
543     }
544 
545     @Override
onNfcRebootDialogConfirmed()546     public void onNfcRebootDialogConfirmed() {
547         final NfcSnoopLogPreferenceController controller =
548                 getDevelopmentOptionsController(NfcSnoopLogPreferenceController.class);
549         controller.onNfcRebootDialogConfirmed();
550 
551         final NfcVerboseVendorLogPreferenceController vendorLogController =
552                 getDevelopmentOptionsController(NfcVerboseVendorLogPreferenceController.class);
553         vendorLogController.onNfcRebootDialogConfirmed();
554     }
555 
556     @Override
onNfcRebootDialogCanceled()557     public void onNfcRebootDialogCanceled() {
558         final NfcSnoopLogPreferenceController controller =
559                 getDevelopmentOptionsController(NfcSnoopLogPreferenceController.class);
560         controller.onNfcRebootDialogCanceled();
561 
562         final NfcVerboseVendorLogPreferenceController vendorLogController =
563                 getDevelopmentOptionsController(NfcVerboseVendorLogPreferenceController.class);
564         vendorLogController.onNfcRebootDialogCanceled();
565     }
566 
567     @Override
onSettingChanged()568     public void onSettingChanged() {
569         final BluetoothSnoopLogFilterProfileMapPreferenceController controllerMap =
570                 getDevelopmentOptionsController(
571                         BluetoothSnoopLogFilterProfileMapPreferenceController.class);
572         final BluetoothSnoopLogFilterProfilePbapPreferenceController controllerPbap =
573                 getDevelopmentOptionsController(
574                         BluetoothSnoopLogFilterProfilePbapPreferenceController.class);
575         controllerMap.onSettingChanged();
576         controllerPbap.onSettingChanged();
577     }
578 
579     @Override
onActivityResult(int requestCode, int resultCode, Intent data)580     public void onActivityResult(int requestCode, int resultCode, Intent data) {
581         boolean handledResult = false;
582         if (requestCode == REQUEST_BIOMETRIC_PROMPT) {
583             if (resultCode == RESULT_OK) {
584                 mIsBiometricsAuthenticated = true;
585                 mSwitchBar.setChecked(true);
586             } else if (resultCode
587                     == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) {
588                 IdentityCheckBiometricErrorDialog.showBiometricErrorDialog(getActivity(),
589                         Utils.BiometricStatus.LOCKOUT, false /* twoFactorAuthentication */);
590             }
591         }
592         for (AbstractPreferenceController controller : mPreferenceControllers) {
593             if (controller instanceof OnActivityResultListener) {
594                 // We do not break early because it is possible for multiple controllers to
595                 // handle the same result code.
596                 handledResult |=
597                         ((OnActivityResultListener) controller).onActivityResult(
598                                 requestCode, resultCode, data);
599             }
600         }
601         if (!handledResult) {
602             super.onActivityResult(requestCode, resultCode, data);
603         }
604     }
605 
606     @Override
getLogTag()607     protected String getLogTag() {
608         return TAG;
609     }
610 
611     @Override
getHelpResource()612     public int getHelpResource() {
613         return 0;
614     }
615 
616     @Override
getPreferenceScreenResId()617     protected int getPreferenceScreenResId() {
618         return Utils.isMonkeyRunning() ? R.xml.placeholder_prefs : R.xml.development_settings;
619     }
620 
621     @Override
createPreferenceControllers(Context context)622     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
623         if (Utils.isMonkeyRunning()) {
624             mPreferenceControllers = new ArrayList<>();
625             return null;
626         }
627         mPreferenceControllers = buildPreferenceControllers(context, getActivity(),
628                 getSettingsLifecycle(), this /* devOptionsDashboardFragment */,
629                 new BluetoothA2dpConfigStore());
630         return mPreferenceControllers;
631     }
632 
registerReceivers()633     private void registerReceivers() {
634         LocalBroadcastManager.getInstance(getContext())
635                 .registerReceiver(mEnableAdbReceiver, new IntentFilter(
636                         AdbPreferenceController.ACTION_ENABLE_ADB_STATE_CHANGED));
637 
638         final IntentFilter filter = new IntentFilter();
639         filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
640         getActivity().registerReceiver(mBluetoothA2dpReceiver, filter);
641     }
642 
unregisterReceivers()643     private void unregisterReceivers() {
644         LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mEnableAdbReceiver);
645         getActivity().unregisterReceiver(mBluetoothA2dpReceiver);
646     }
647 
enableDeveloperOptions()648     private void enableDeveloperOptions() {
649         if (Utils.isMonkeyRunning()) {
650             return;
651         }
652         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(getContext(), true);
653         for (AbstractPreferenceController controller : mPreferenceControllers) {
654             if (controller instanceof DeveloperOptionsPreferenceController) {
655                 ((DeveloperOptionsPreferenceController) controller).onDeveloperOptionsEnabled();
656             }
657         }
658     }
659 
disableDeveloperOptions()660     private void disableDeveloperOptions() {
661         if (Utils.isMonkeyRunning()) {
662             return;
663         }
664 
665         // Disabling developer options in page-agnostic mode isn't supported as device isn't in
666         // production state
667         if (Enable16kUtils.isPageAgnosticModeOn(getContext())) {
668             Enable16kUtils.showPageAgnosticWarning(getContext());
669             onDisableDevelopmentOptionsRejected();
670             return;
671         }
672 
673         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(getContext(), false);
674         final SystemPropPoker poker = SystemPropPoker.getInstance();
675         poker.blockPokes();
676         for (AbstractPreferenceController controller : mPreferenceControllers) {
677             if (controller instanceof DeveloperOptionsPreferenceController) {
678                 ((DeveloperOptionsPreferenceController) controller)
679                         .onDeveloperOptionsDisabled();
680             }
681         }
682         poker.unblockPokes();
683         poker.poke();
684     }
685 
onEnableDevelopmentOptionsConfirmed()686     void onEnableDevelopmentOptionsConfirmed() {
687         enableDeveloperOptions();
688     }
689 
onEnableDevelopmentOptionsRejected()690     void onEnableDevelopmentOptionsRejected() {
691         // Reset the toggle
692         mSwitchBar.setChecked(false);
693     }
694 
onDisableDevelopmentOptionsConfirmed()695     void onDisableDevelopmentOptionsConfirmed() {
696         disableDeveloperOptions();
697     }
698 
onDisableDevelopmentOptionsRejected()699     void onDisableDevelopmentOptionsRejected() {
700         // Reset the toggle
701         mSwitchBar.setChecked(true);
702     }
703 
buildPreferenceControllers(Context context, @Nullable Activity activity, @Nullable Lifecycle lifecycle, @Nullable DevelopmentSettingsDashboardFragment fragment, @Nullable BluetoothA2dpConfigStore bluetoothA2dpConfigStore)704     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
705             @Nullable Activity activity, @Nullable Lifecycle lifecycle,
706             @Nullable DevelopmentSettingsDashboardFragment fragment,
707             @Nullable BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
708         final List<AbstractPreferenceController> controllers = new ArrayList<>();
709         controllers.add(new MemoryUsagePreferenceController(context));
710         controllers.add(new BugReportPreferenceController(context));
711         controllers.add(new BugReportHandlerPreferenceController(context));
712         controllers.add(new SystemServerHeapDumpPreferenceController(context));
713         controllers.add(new DevelopmentMemtagPagePreferenceController(context));
714         controllers.add(new LocalBackupPasswordPreferenceController(context));
715         controllers.add(new StayAwakePreferenceController(context, lifecycle));
716         controllers.add(new HdcpCheckingPreferenceController(context));
717         controllers.add(new BluetoothSnoopLogPreferenceController(context, fragment));
718         controllers.add(new BluetoothStackLogPreferenceController(context));
719         controllers.add(new DefaultLaunchPreferenceController(context,
720                 "snoop_logger_filters_dashboard"));
721         controllers.add(new BluetoothSnoopLogFilterProfilePbapPreferenceController(context));
722         controllers.add(new BluetoothSnoopLogFilterProfileMapPreferenceController(context));
723         controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
724         controllers.add(new Enable16kPagesPreferenceController(context, fragment));
725         controllers.add(new PictureColorModePreferenceController(context, lifecycle));
726         controllers.add(new WebViewAppPreferenceController(context));
727         controllers.add(new WebViewDevUiPreferenceController(context));
728         controllers.add(new CoolColorTemperaturePreferenceController(context));
729         controllers.add(new DisableAutomaticUpdatesPreferenceController(context));
730         controllers.add(new SelectDSUPreferenceController(context));
731         controllers.add(new AdbPreferenceController(context, fragment));
732         controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
733         controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
734         controllers.add(new AdbAuthorizationTimeoutPreferenceController(context));
735         controllers.add(new LocalTerminalPreferenceController(context));
736         controllers.add(new LinuxTerminalPreferenceController(context));
737         controllers.add(new BugReportInPowerPreferenceController(context));
738         controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
739         controllers.add(new MockLocationAppPreferenceController(context, fragment));
740         controllers.add(new MockModemPreferenceController(context));
741         controllers.add(new DebugViewAttributesPreferenceController(context));
742         controllers.add(new SelectDebugAppPreferenceController(context, fragment));
743         controllers.add(new WaitForDebuggerPreferenceController(context));
744         controllers.add(new EnableGpuDebugLayersPreferenceController(context));
745         controllers.add(new GraphicsDriverEnableAngleAsSystemDriverController(context, fragment));
746         controllers.add(new ForcePeakRefreshRatePreferenceController(context));
747         controllers.add(new EnableVerboseVendorLoggingPreferenceController(context));
748         controllers.add(new VerifyAppsOverUsbPreferenceController(context));
749         controllers.add(new ArtVerifierPreferenceController(context));
750         controllers.add(new LogdSizePreferenceController(context));
751         controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle));
752         controllers.add(new CameraLaserSensorPreferenceController(context));
753         controllers.add(new WifiDisplayCertificationPreferenceController(context));
754         controllers.add(new WifiVerboseLoggingPreferenceController(context));
755         controllers.add(new WifiScanThrottlingPreferenceController(context));
756         controllers.add(new WifiNonPersistentMacRandomizationPreferenceController(context));
757         controllers.add(new MobileDataAlwaysOnPreferenceController(context));
758         controllers.add(new TetheringHardwareAccelPreferenceController(context));
759         controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
760         controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
761         controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
762         controllers.add(new BluetoothMapVersionPreferenceController(context));
763         controllers.add(new BluetoothLeAudioPreferenceController(context, fragment));
764         controllers.add(new BluetoothLeAudioModePreferenceController(context, fragment));
765         controllers.add(new BluetoothLeAudioUiPreferenceController(context, fragment));
766         controllers.add(new BluetoothLeAudioDeviceDetailsPreferenceController(context));
767         controllers.add(new BluetoothLeAudioAllowListPreferenceController(context));
768         controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
769         controllers.add(new BluetoothLeAudioHwOffloadPreferenceController(context, fragment));
770         controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
771         controllers.add(new NfcSnoopLogPreferenceController(context, fragment));
772         controllers.add(new NfcVerboseVendorLogPreferenceController(context, fragment));
773         controllers.add(new ShowTapsPreferenceController(context));
774         controllers.add(new PointerLocationPreferenceController(context));
775         controllers.add(new ShowKeyPressesPreferenceController(context));
776         controllers.add(new TouchpadVisualizerPreferenceController(context));
777         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
778         controllers.add(new ShowLayoutBoundsPreferenceController(context));
779         controllers.add(new ShowHdrSdrRatioPreferenceController(context));
780         controllers.add(new ShowRefreshRatePreferenceController(context));
781         controllers.add(new RtlLayoutPreferenceController(context));
782         controllers.add(new WindowAnimationScalePreferenceController(context));
783         controllers.add(new EmulateDisplayCutoutPreferenceController(context));
784         controllers.add(new TransparentNavigationBarPreferenceController(context));
785         controllers.add(new TransitionAnimationScalePreferenceController(context));
786         controllers.add(new AnimatorDurationScalePreferenceController(context));
787         controllers.add(new SecondaryDisplayPreferenceController(context));
788         controllers.add(new GpuViewUpdatesPreferenceController(context));
789         controllers.add(new HardwareLayersUpdatesPreferenceController(context));
790         controllers.add(new DebugGpuOverdrawPreferenceController(context));
791         controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
792         controllers.add(new GameDefaultFrameRatePreferenceController(context));
793         controllers.add(new ForceDarkPreferenceController(context));
794         controllers.add(new EnableBlursPreferenceController(context));
795         controllers.add(new ForceMSAAPreferenceController(context));
796         controllers.add(new HardwareOverlaysPreferenceController(context));
797         controllers.add(new SimulateColorSpacePreferenceController(context));
798         controllers.add(new UsbAudioRoutingPreferenceController(context));
799         controllers.add(new StrictModePreferenceController(context));
800         controllers.add(new ProfileGpuRenderingPreferenceController(context));
801         controllers.add(new KeepActivitiesPreferenceController(context));
802         controllers.add(new BackgroundProcessLimitPreferenceController(context));
803         controllers.add(new CachedAppsFreezerPreferenceController(context));
804         controllers.add(new ShowFirstCrashDialogPreferenceController(context));
805         controllers.add(new AppsNotRespondingPreferenceController(context));
806         controllers.add(new NotificationChannelWarningsPreferenceController(context));
807         controllers.add(new AllowAppsOnExternalPreferenceController(context));
808         controllers.add(new ResizableActivityPreferenceController(context));
809         controllers.add(new FreeformWindowsPreferenceController(context, fragment));
810         controllers.add(new DesktopModePreferenceController(context, fragment));
811         controllers.add(new DesktopModeSecondaryDisplayPreferenceController(context, fragment));
812         controllers.add(new DesktopExperiencePreferenceController(context, fragment));
813         controllers.add(new NonResizableMultiWindowPreferenceController(context));
814         controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
815         controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
816         controllers.add(new DefaultLaunchPreferenceController(context, "running_apps"));
817         controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode"));
818         controllers.add(new DefaultLaunchPreferenceController(context, "quick_settings_tiles"));
819         controllers.add(new DefaultLaunchPreferenceController(context, "feature_flags_dashboard"));
820         controllers.add(new DefaultUsbConfigurationPreferenceController(context));
821         controllers.add(new DefaultLaunchPreferenceController(context, "density"));
822         controllers.add(new DefaultLaunchPreferenceController(context, "background_check"));
823         controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps"));
824         controllers.add(new AutofillCategoryController(context, lifecycle));
825         controllers.add(new AutofillLoggingLevelPreferenceController(context, lifecycle));
826         controllers.add(new AutofillResetOptionsPreferenceController(context));
827         controllers.add(
828                 new BluetoothCodecListPreferenceController(
829                         context, lifecycle, bluetoothA2dpConfigStore, fragment));
830         controllers.add(new BluetoothSampleRateDialogPreferenceController(context, lifecycle,
831                 bluetoothA2dpConfigStore));
832         controllers.add(new BluetoothBitPerSampleDialogPreferenceController(context, lifecycle,
833                 bluetoothA2dpConfigStore));
834         controllers.add(new BluetoothQualityDialogPreferenceController(context, lifecycle,
835                 bluetoothA2dpConfigStore));
836         controllers.add(new BluetoothChannelModeDialogPreferenceController(context, lifecycle,
837                 bluetoothA2dpConfigStore));
838         controllers.add(new BluetoothHDAudioPreferenceController(context, lifecycle,
839                 bluetoothA2dpConfigStore, fragment));
840         controllers.add(new SharedDataPreferenceController(context));
841         controllers.add(new OverlaySettingsPreferenceController(context));
842         controllers.add(new StylusHandwritingPreferenceController(context));
843         controllers.add(new IngressRateLimitPreferenceController((context)));
844         controllers.add(new PhantomProcessPreferenceController(context));
845         controllers.add(new ForceEnableNotesRolePreferenceController(context));
846         controllers.add(new GrammaticalGenderPreferenceController(context));
847         controllers.add(new SensitiveContentProtectionPreferenceController(context));
848         controllers.add(new ShadeDisplayAwarenessPreferenceController(context));
849 
850         return controllers;
851     }
852 
853     @VisibleForTesting
getDevelopmentOptionsController(Class<T> clazz)854     <T extends AbstractPreferenceController> T getDevelopmentOptionsController(Class<T> clazz) {
855         return use(clazz);
856     }
857 
858     @Override
onBluetoothCodecChanged()859     public void onBluetoothCodecChanged() {
860         for (AbstractPreferenceController controller : mPreferenceControllers) {
861             if (controller instanceof AbstractBluetoothDialogPreferenceController) {
862                 ((AbstractBluetoothDialogPreferenceController) controller)
863                         .onBluetoothCodecUpdated();
864             }
865         }
866     }
867 
868     @Override
onBluetoothHDAudioEnabled(boolean enabled)869     public void onBluetoothHDAudioEnabled(boolean enabled) {
870         for (AbstractPreferenceController controller : mPreferenceControllers) {
871             if (controller instanceof AbstractBluetoothDialogPreferenceController) {
872                 ((AbstractBluetoothDialogPreferenceController) controller).onHDAudioEnabled(
873                         enabled);
874             }
875             if (controller instanceof BluetoothCodecListPreferenceController) {
876                 ((BluetoothCodecListPreferenceController) controller).onHDAudioEnabled(enabled);
877             }
878         }
879     }
880 
881     @Override
onBroadcastDisabled()882     public void onBroadcastDisabled() {
883         for (AbstractPreferenceController controller : mPreferenceControllers) {
884             if (controller instanceof BluetoothLeAudioUiPreferenceController) {
885                 ((BluetoothLeAudioUiPreferenceController) controller).onBroadcastDisabled();
886             }
887         }
888     }
889 
890     /**
891      * For Search.
892      */
893     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
894             new BaseSearchIndexProvider(R.xml.development_settings) {
895 
896                 @Override
897                 protected boolean isPageSearchEnabled(Context context) {
898                     return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
899                 }
900 
901                 @Override
902                 public List<AbstractPreferenceController> createPreferenceControllers(Context
903                         context) {
904                     return buildPreferenceControllers(context, null /* activity */,
905                             null /* lifecycle */, null /* devOptionsDashboardFragment */,
906                             null /* bluetoothA2dpConfigStore */);
907                 }
908             };
909 }
910