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
17package com.android.settings.development;
18
19import android.app.Activity;
20import android.bluetooth.BluetoothA2dp;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothCodecStatus;
23import android.bluetooth.BluetoothProfile;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.os.Bundle;
29import android.os.UserManager;
30import android.provider.SearchIndexableResource;
31import android.support.annotation.VisibleForTesting;
32import android.support.v4.content.LocalBroadcastManager;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.Switch;
38
39import com.android.internal.logging.nano.MetricsProto;
40import com.android.settings.R;
41import com.android.settings.SettingsActivity;
42import com.android.settings.Utils;
43import com.android.settings.dashboard.RestrictedDashboardFragment;
44import com.android.settings.search.BaseSearchIndexProvider;
45import com.android.settings.search.Indexable;
46import com.android.settings.widget.SwitchBar;
47import com.android.settingslib.core.AbstractPreferenceController;
48import com.android.settingslib.core.lifecycle.Lifecycle;
49import com.android.settingslib.development.DeveloperOptionsPreferenceController;
50import com.android.settingslib.development.DevelopmentSettingsEnabler;
51import com.android.settingslib.development.SystemPropPoker;
52
53import java.util.ArrayList;
54import java.util.Arrays;
55import java.util.List;
56
57public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
58        implements SwitchBar.OnSwitchChangeListener, OemUnlockDialogHost, AdbDialogHost,
59        AdbClearKeysDialogHost, LogPersistDialogHost,
60        BluetoothA2dpHwOffloadRebootDialog.OnA2dpHwDialogConfirmedListener {
61
62    private static final String TAG = "DevSettingsDashboard";
63
64    private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore =
65            new BluetoothA2dpConfigStore();
66
67    private boolean mIsAvailable = true;
68    private SwitchBar mSwitchBar;
69    private DevelopmentSwitchBarController mSwitchBarController;
70    private List<AbstractPreferenceController> mPreferenceControllers = new ArrayList<>();
71    private BluetoothA2dp mBluetoothA2dp;
72
73    private final BroadcastReceiver mEnableAdbReceiver = new BroadcastReceiver() {
74        @Override
75        public void onReceive(Context context, Intent intent) {
76            for (AbstractPreferenceController controller : mPreferenceControllers) {
77                if (controller instanceof AdbOnChangeListener) {
78                    ((AdbOnChangeListener) controller).onAdbSettingChanged();
79                }
80            }
81        }
82    };
83
84    private final BroadcastReceiver mBluetoothA2dpReceiver = new BroadcastReceiver() {
85        @Override
86        public void onReceive(Context context, Intent intent) {
87            Log.d(TAG, "mBluetoothA2dpReceiver.onReceive intent=" + intent);
88            String action = intent.getAction();
89
90            if (BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED.equals(action)) {
91                BluetoothCodecStatus codecStatus = intent.getParcelableExtra(
92                        BluetoothCodecStatus.EXTRA_CODEC_STATUS);
93                Log.d(TAG, "Received BluetoothCodecStatus=" + codecStatus);
94                for (AbstractPreferenceController controller : mPreferenceControllers) {
95                    if (controller instanceof BluetoothServiceConnectionListener) {
96                        ((BluetoothServiceConnectionListener) controller).onBluetoothCodecUpdated();
97                    }
98                }
99            }
100        }
101    };
102
103
104    private final BluetoothProfile.ServiceListener mBluetoothA2dpServiceListener =
105            new BluetoothProfile.ServiceListener() {
106                @Override
107                public void onServiceConnected(int profile,
108                        BluetoothProfile proxy) {
109                    synchronized (mBluetoothA2dpConfigStore) {
110                        mBluetoothA2dp = (BluetoothA2dp) proxy;
111                    }
112                    for (AbstractPreferenceController controller : mPreferenceControllers) {
113                        if (controller instanceof BluetoothServiceConnectionListener) {
114                            ((BluetoothServiceConnectionListener) controller)
115                                    .onBluetoothServiceConnected(mBluetoothA2dp);
116                        }
117                    }
118                }
119
120                @Override
121                public void onServiceDisconnected(int profile) {
122                    synchronized (mBluetoothA2dpConfigStore) {
123                        mBluetoothA2dp = null;
124                    }
125                    for (AbstractPreferenceController controller : mPreferenceControllers) {
126                        if (controller instanceof BluetoothServiceConnectionListener) {
127                            ((BluetoothServiceConnectionListener) controller)
128                                    .onBluetoothServiceDisconnected();
129                        }
130                    }
131                }
132            };
133
134    public DevelopmentSettingsDashboardFragment() {
135        super(UserManager.DISALLOW_DEBUGGING_FEATURES);
136    }
137
138    @Override
139    public void onCreate(Bundle icicle) {
140        super.onCreate(icicle);
141        if (Utils.isMonkeyRunning()) {
142            getActivity().finish();
143            return;
144        }
145    }
146
147    @Override
148    public void onActivityCreated(Bundle icicle) {
149        super.onActivityCreated(icicle);
150        // Apply page-level restrictions
151        setIfOnlyAvailableForAdmins(true);
152        if (isUiRestricted() || !Utils.isDeviceProvisioned(getActivity())) {
153            // Block access to developer options if the user is not the owner, if user policy
154            // restricts it, or if the device has not been provisioned
155            mIsAvailable = false;
156            // Show error message
157            if (!isUiRestrictedByOnlyAdmin()) {
158                getEmptyTextView().setText(R.string.development_settings_not_available);
159            }
160            getPreferenceScreen().removeAll();
161            return;
162        }
163        // Set up master switch
164        mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
165        mSwitchBarController = new DevelopmentSwitchBarController(
166                this /* DevelopmentSettings */, mSwitchBar, mIsAvailable, getLifecycle());
167        mSwitchBar.show();
168
169        // Restore UI state based on whether developer options is enabled
170        if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
171            enableDeveloperOptions();
172        } else {
173            disableDeveloperOptions();
174        }
175    }
176
177    @Override
178    public View onCreateView(LayoutInflater inflater, ViewGroup container,
179            Bundle savedInstanceState) {
180        registerReceivers();
181
182        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
183        if (adapter != null) {
184            adapter.getProfileProxy(getActivity(), mBluetoothA2dpServiceListener,
185                    BluetoothProfile.A2DP);
186        }
187        return super.onCreateView(inflater, container, savedInstanceState);
188    }
189
190    @Override
191    public void onDestroyView() {
192        super.onDestroyView();
193        unregisterReceivers();
194
195        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
196        if (adapter != null) {
197            adapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
198            mBluetoothA2dp = null;
199        }
200    }
201
202    @Override
203    public int getMetricsCategory() {
204        return MetricsProto.MetricsEvent.DEVELOPMENT;
205    }
206
207    @Override
208    public void onSwitchChanged(Switch switchView, boolean isChecked) {
209        if (switchView != mSwitchBar.getSwitch()) {
210            return;
211        }
212        final boolean developmentEnabledState =
213                DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext());
214        if (isChecked != developmentEnabledState) {
215            if (isChecked) {
216                EnableDevelopmentSettingWarningDialog.show(this /* host */);
217            } else {
218                disableDeveloperOptions();
219            }
220        }
221    }
222
223    @Override
224    public void onOemUnlockDialogConfirmed() {
225        final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
226                OemUnlockPreferenceController.class);
227        controller.onOemUnlockConfirmed();
228    }
229
230    @Override
231    public void onOemUnlockDialogDismissed() {
232        final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
233                OemUnlockPreferenceController.class);
234        controller.onOemUnlockDismissed();
235    }
236
237    @Override
238    public void onEnableAdbDialogConfirmed() {
239        final AdbPreferenceController controller = getDevelopmentOptionsController(
240                AdbPreferenceController.class);
241        controller.onAdbDialogConfirmed();
242
243    }
244
245    @Override
246    public void onEnableAdbDialogDismissed() {
247        final AdbPreferenceController controller = getDevelopmentOptionsController(
248                AdbPreferenceController.class);
249        controller.onAdbDialogDismissed();
250    }
251
252    @Override
253    public void onAdbClearKeysDialogConfirmed() {
254        final ClearAdbKeysPreferenceController controller = getDevelopmentOptionsController(
255                ClearAdbKeysPreferenceController.class);
256        controller.onClearAdbKeysConfirmed();
257    }
258
259    @Override
260    public void onDisableLogPersistDialogConfirmed() {
261        final LogPersistPreferenceController controller = getDevelopmentOptionsController(
262                LogPersistPreferenceController.class);
263        controller.onDisableLogPersistDialogConfirmed();
264    }
265
266    @Override
267    public void onDisableLogPersistDialogRejected() {
268        final LogPersistPreferenceController controller = getDevelopmentOptionsController(
269                LogPersistPreferenceController.class);
270        controller.onDisableLogPersistDialogRejected();
271    }
272
273    @Override
274    public void onA2dpHwDialogConfirmed() {
275        final BluetoothA2dpHwOffloadPreferenceController controller =
276                getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
277        controller.onA2dpHwDialogConfirmed();
278    }
279
280    @Override
281    public void onActivityResult(int requestCode, int resultCode, Intent data) {
282        boolean handledResult = false;
283        for (AbstractPreferenceController controller : mPreferenceControllers) {
284            if (controller instanceof OnActivityResultListener) {
285                // We do not break early because it is possible for multiple controllers to
286                // handle the same result code.
287                handledResult |=
288                        ((OnActivityResultListener) controller).onActivityResult(
289                                requestCode, resultCode, data);
290            }
291        }
292        if (!handledResult) {
293            super.onActivityResult(requestCode, resultCode, data);
294        }
295    }
296
297    @Override
298    protected String getLogTag() {
299        return TAG;
300    }
301
302    @Override
303    public int getHelpResource() {
304        return 0;
305    }
306
307    @Override
308    protected int getPreferenceScreenResId() {
309        return Utils.isMonkeyRunning()? R.xml.placeholder_prefs : R.xml.development_settings;
310    }
311
312    @Override
313    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
314        if (Utils.isMonkeyRunning()) {
315            mPreferenceControllers = new ArrayList<>();
316            return null;
317        }
318        mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(),
319                this /* devOptionsDashboardFragment */,
320                new BluetoothA2dpConfigStore());
321        return mPreferenceControllers;
322    }
323
324    private void registerReceivers() {
325        LocalBroadcastManager.getInstance(getContext())
326                .registerReceiver(mEnableAdbReceiver, new IntentFilter(
327                        AdbPreferenceController.ACTION_ENABLE_ADB_STATE_CHANGED));
328
329        final IntentFilter filter = new IntentFilter();
330        filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
331        getActivity().registerReceiver(mBluetoothA2dpReceiver, filter);
332    }
333
334    private void unregisterReceivers() {
335        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mEnableAdbReceiver);
336        getActivity().unregisterReceiver(mBluetoothA2dpReceiver);
337    }
338
339    private void enableDeveloperOptions() {
340        if (Utils.isMonkeyRunning()) {
341            return;
342        }
343        DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(getContext(), true);
344        for (AbstractPreferenceController controller : mPreferenceControllers) {
345            if (controller instanceof DeveloperOptionsPreferenceController) {
346                ((DeveloperOptionsPreferenceController) controller).onDeveloperOptionsEnabled();
347            }
348        }
349    }
350
351    private void disableDeveloperOptions() {
352        if (Utils.isMonkeyRunning()) {
353            return;
354        }
355        DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(getContext(), false);
356        final SystemPropPoker poker = SystemPropPoker.getInstance();
357        poker.blockPokes();
358        for (AbstractPreferenceController controller : mPreferenceControllers) {
359            if (controller instanceof DeveloperOptionsPreferenceController) {
360                ((DeveloperOptionsPreferenceController) controller)
361                        .onDeveloperOptionsDisabled();
362            }
363        }
364        poker.unblockPokes();
365        poker.poke();
366    }
367
368    void onEnableDevelopmentOptionsConfirmed() {
369        enableDeveloperOptions();
370    }
371
372    void onEnableDevelopmentOptionsRejected() {
373        // Reset the toggle
374        mSwitchBar.setChecked(false);
375    }
376
377    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
378            Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
379            BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
380        final List<AbstractPreferenceController> controllers = new ArrayList<>();
381        controllers.add(new MemoryUsagePreferenceController(context));
382        controllers.add(new BugReportPreferenceController(context));
383        controllers.add(new LocalBackupPasswordPreferenceController(context));
384        controllers.add(new StayAwakePreferenceController(context, lifecycle));
385        controllers.add(new HdcpCheckingPreferenceController(context));
386        controllers.add(new DarkUIPreferenceController(context));
387        controllers.add(new BluetoothSnoopLogPreferenceController(context));
388        controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
389        controllers.add(new FileEncryptionPreferenceController(context));
390        controllers.add(new PictureColorModePreferenceController(context, lifecycle));
391        controllers.add(new WebViewAppPreferenceController(context));
392        controllers.add(new CoolColorTemperaturePreferenceController(context));
393        controllers.add(new DisableAutomaticUpdatesPreferenceController(context));
394        controllers.add(new AdbPreferenceController(context, fragment));
395        controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
396        controllers.add(new LocalTerminalPreferenceController(context));
397        controllers.add(new BugReportInPowerPreferenceController(context));
398        controllers.add(new MockLocationAppPreferenceController(context, fragment));
399        controllers.add(new DebugViewAttributesPreferenceController(context));
400        controllers.add(new SelectDebugAppPreferenceController(context, fragment));
401        controllers.add(new WaitForDebuggerPreferenceController(context));
402        controllers.add(new EnableGpuDebugLayersPreferenceController(context));
403        controllers.add(new VerifyAppsOverUsbPreferenceController(context));
404        controllers.add(new LogdSizePreferenceController(context));
405        controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle));
406        controllers.add(new CameraLaserSensorPreferenceController(context));
407        controllers.add(new WifiDisplayCertificationPreferenceController(context));
408        controllers.add(new WifiVerboseLoggingPreferenceController(context));
409        controllers.add(new WifiConnectedMacRandomizationPreferenceController(context));
410        controllers.add(new MobileDataAlwaysOnPreferenceController(context));
411        controllers.add(new TetheringHardwareAccelPreferenceController(context));
412        controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
413        controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
414        controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
415        controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
416        controllers.add(new BluetoothAudioCodecPreferenceController(context, lifecycle,
417                bluetoothA2dpConfigStore));
418        controllers.add(new BluetoothAudioSampleRatePreferenceController(context, lifecycle,
419                bluetoothA2dpConfigStore));
420        controllers.add(new BluetoothAudioBitsPerSamplePreferenceController(context, lifecycle,
421                bluetoothA2dpConfigStore));
422        controllers.add(new BluetoothAudioChannelModePreferenceController(context, lifecycle,
423                bluetoothA2dpConfigStore));
424        controllers.add(new BluetoothAudioQualityPreferenceController(context, lifecycle,
425                bluetoothA2dpConfigStore));
426        controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
427        controllers.add(new ShowTapsPreferenceController(context));
428        controllers.add(new PointerLocationPreferenceController(context));
429        controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
430        controllers.add(new ShowLayoutBoundsPreferenceController(context));
431        controllers.add(new RtlLayoutPreferenceController(context));
432        controllers.add(new WindowAnimationScalePreferenceController(context));
433        controllers.add(new EmulateDisplayCutoutPreferenceController(context));
434        controllers.add(new TransitionAnimationScalePreferenceController(context));
435        controllers.add(new AnimatorDurationScalePreferenceController(context));
436        controllers.add(new SecondaryDisplayPreferenceController(context));
437        controllers.add(new ForceGpuRenderingPreferenceController(context));
438        controllers.add(new GpuViewUpdatesPreferenceController(context));
439        controllers.add(new HardwareLayersUpdatesPreferenceController(context));
440        controllers.add(new DebugGpuOverdrawPreferenceController(context));
441        controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
442        controllers.add(new ForceMSAAPreferenceController(context));
443        controllers.add(new HardwareOverlaysPreferenceController(context));
444        controllers.add(new SimulateColorSpacePreferenceController(context));
445        controllers.add(new UsbAudioRoutingPreferenceController(context));
446        controllers.add(new StrictModePreferenceController(context));
447        controllers.add(new ProfileGpuRenderingPreferenceController(context));
448        controllers.add(new KeepActivitiesPreferenceController(context));
449        controllers.add(new BackgroundProcessLimitPreferenceController(context));
450        controllers.add(new ShowFirstCrashDialogPreferenceController(context));
451        controllers.add(new AppsNotRespondingPreferenceController(context));
452        controllers.add(new NotificationChannelWarningsPreferenceController(context));
453        controllers.add(new AllowAppsOnExternalPreferenceController(context));
454        controllers.add(new ResizableActivityPreferenceController(context));
455        controllers.add(new FreeformWindowsPreferenceController(context));
456        controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
457        controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
458        controllers.add(new DefaultLaunchPreferenceController(context, "running_apps"));
459        controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode"));
460        controllers.add(new DefaultLaunchPreferenceController(context, "quick_settings_tiles"));
461        controllers.add(new DefaultLaunchPreferenceController(context, "feature_flags_dashboard"));
462        controllers.add(
463            new DefaultLaunchPreferenceController(context, "default_usb_configuration"));
464        controllers.add(new DefaultLaunchPreferenceController(context, "density"));
465        controllers.add(new DefaultLaunchPreferenceController(context, "background_check"));
466        controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps"));
467        return controllers;
468    }
469
470    @VisibleForTesting
471    <T extends AbstractPreferenceController> T getDevelopmentOptionsController(Class<T> clazz) {
472        return use(clazz);
473    }
474
475    /**
476     * For Search.
477     */
478    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
479            new BaseSearchIndexProvider() {
480
481                @Override
482                protected boolean isPageSearchEnabled(Context context) {
483                    return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
484                }
485
486                @Override
487                public List<SearchIndexableResource> getXmlResourcesToIndex(
488                        Context context, boolean enabled) {
489
490                    final SearchIndexableResource sir = new SearchIndexableResource(context);
491                    sir.xmlResId = R.xml.development_settings;
492                    return Arrays.asList(sir);
493                }
494
495                @Override
496                public List<AbstractPreferenceController> createPreferenceControllers(Context
497                        context) {
498                    return buildPreferenceControllers(context, null /* activity */,
499                            null /* lifecycle */, null /* devOptionsDashboardFragment */,
500                            null /* bluetoothA2dpConfigStore */);
501                }
502            };
503}
504