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