DevelopmentSettings.java revision f04211443e9cae815ad89f821b6ec83ec2790c0c
1/*
2 * Copyright (C) 2008 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;
18
19import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
20
21import android.app.ActionBar;
22import android.app.Activity;
23import android.app.ActivityManagerNative;
24import android.app.ActivityThread;
25import android.app.AlertDialog;
26import android.app.Dialog;
27import android.app.DialogFragment;
28import android.app.admin.DevicePolicyManager;
29import android.app.backup.IBackupManager;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.DialogInterface.OnClickListener;
34import android.content.Intent;
35import android.content.pm.ApplicationInfo;
36import android.content.pm.PackageManager;
37import android.content.pm.ResolveInfo;
38import android.os.AsyncTask;
39import android.os.BatteryManager;
40import android.os.Build;
41import android.os.Bundle;
42import android.os.IBinder;
43import android.os.Parcel;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.StrictMode;
47import android.os.SystemProperties;
48import android.os.Trace;
49import android.preference.CheckBoxPreference;
50import android.preference.ListPreference;
51import android.preference.MultiCheckPreference;
52import android.preference.Preference;
53import android.preference.Preference.OnPreferenceChangeListener;
54import android.preference.PreferenceFragment;
55import android.preference.PreferenceGroup;
56import android.preference.PreferenceScreen;
57import android.provider.Settings;
58import android.text.TextUtils;
59import android.view.Gravity;
60import android.view.HardwareRenderer;
61import android.view.IWindowManager;
62import android.view.View;
63import android.widget.CompoundButton;
64import android.widget.Switch;
65
66import java.util.ArrayList;
67import java.util.HashSet;
68import java.util.List;
69
70/*
71 * Displays preferences for application developers.
72 */
73public class DevelopmentSettings extends PreferenceFragment
74        implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
75                OnPreferenceChangeListener, CompoundButton.OnCheckedChangeListener {
76
77    private static final String ENABLE_ADB = "enable_adb";
78    private static final String KEEP_SCREEN_ON = "keep_screen_on";
79    private static final String ALLOW_MOCK_LOCATION = "allow_mock_location";
80    private static final String HDCP_CHECKING_KEY = "hdcp_checking";
81    private static final String HDCP_CHECKING_PROPERTY = "persist.sys.hdcp_checking";
82    private static final String ENFORCE_READ_EXTERNAL = "enforce_read_external";
83    private static final String LOCAL_BACKUP_PASSWORD = "local_backup_password";
84    private static final String HARDWARE_UI_PROPERTY = "persist.sys.ui.hw";
85    private static final String BUGREPORT_IN_POWER_KEY = "bugreport_in_power";
86
87    private static final String DEBUG_APP_KEY = "debug_app";
88    private static final String WAIT_FOR_DEBUGGER_KEY = "wait_for_debugger";
89    private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb";
90    private static final String STRICT_MODE_KEY = "strict_mode";
91    private static final String POINTER_LOCATION_KEY = "pointer_location";
92    private static final String SHOW_TOUCHES_KEY = "show_touches";
93    private static final String SHOW_SCREEN_UPDATES_KEY = "show_screen_updates";
94    private static final String DISABLE_OVERLAYS_KEY = "disable_overlays";
95    private static final String SHOW_CPU_USAGE_KEY = "show_cpu_usage";
96    private static final String FORCE_HARDWARE_UI_KEY = "force_hw_ui";
97    private static final String TRACK_FRAME_TIME_KEY = "track_frame_time";
98    private static final String SHOW_HW_SCREEN_UPDATES_KEY = "show_hw_screen_udpates";
99    private static final String SHOW_HW_LAYERS_UPDATES_KEY = "show_hw_layers_udpates";
100    private static final String SHOW_HW_OVERDRAW_KEY = "show_hw_overdraw";
101    private static final String DEBUG_LAYOUT_KEY = "debug_layout";
102    private static final String WINDOW_ANIMATION_SCALE_KEY = "window_animation_scale";
103    private static final String TRANSITION_ANIMATION_SCALE_KEY = "transition_animation_scale";
104    private static final String ANIMATOR_DURATION_SCALE_KEY = "animator_duration_scale";
105    private static final String OVERLAY_DISPLAY_DEVICES_KEY = "overlay_display_devices";
106    private static final String DEBUG_DEBUGGING_CATEGORY_KEY = "debug_debugging_category";
107
108    private static final String ENABLE_TRACES_KEY = "enable_traces";
109
110    private static final String IMMEDIATELY_DESTROY_ACTIVITIES_KEY
111            = "immediately_destroy_activities";
112    private static final String APP_PROCESS_LIMIT_KEY = "app_process_limit";
113
114    private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs";
115
116    private static final String TAG_CONFIRM_ENFORCE = "confirm_enforce";
117
118    private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
119
120    private static final int RESULT_DEBUG_APP = 1000;
121
122    private IWindowManager mWindowManager;
123    private IBackupManager mBackupManager;
124    private DevicePolicyManager mDpm;
125
126    private Switch mEnabledSwitch;
127    private boolean mLastEnabledState;
128    private boolean mHaveDebugSettings;
129    private boolean mDontPokeProperties;
130
131    private CheckBoxPreference mEnableAdb;
132    private CheckBoxPreference mBugreportInPower;
133    private CheckBoxPreference mKeepScreenOn;
134    private CheckBoxPreference mEnforceReadExternal;
135    private CheckBoxPreference mAllowMockLocation;
136    private PreferenceScreen mPassword;
137
138    private String mDebugApp;
139    private Preference mDebugAppPref;
140    private CheckBoxPreference mWaitForDebugger;
141    private CheckBoxPreference mVerifyAppsOverUsb;
142
143    private CheckBoxPreference mStrictMode;
144    private CheckBoxPreference mPointerLocation;
145    private CheckBoxPreference mShowTouches;
146    private CheckBoxPreference mShowScreenUpdates;
147    private CheckBoxPreference mDisableOverlays;
148    private CheckBoxPreference mShowCpuUsage;
149    private CheckBoxPreference mForceHardwareUi;
150    private CheckBoxPreference mTrackFrameTime;
151    private CheckBoxPreference mShowHwScreenUpdates;
152    private CheckBoxPreference mShowHwLayersUpdates;
153    private CheckBoxPreference mShowHwOverdraw;
154    private CheckBoxPreference mDebugLayout;
155    private ListPreference mWindowAnimationScale;
156    private ListPreference mTransitionAnimationScale;
157    private ListPreference mAnimatorDurationScale;
158    private ListPreference mOverlayDisplayDevices;
159    private MultiCheckPreference mEnableTracesPref;
160
161    private CheckBoxPreference mImmediatelyDestroyActivities;
162    private ListPreference mAppProcessLimit;
163
164    private CheckBoxPreference mShowAllANRs;
165
166    private final ArrayList<Preference> mAllPrefs = new ArrayList<Preference>();
167    private final ArrayList<CheckBoxPreference> mResetCbPrefs
168            = new ArrayList<CheckBoxPreference>();
169
170    private final HashSet<Preference> mDisabledPrefs = new HashSet<Preference>();
171
172    // To track whether a confirmation dialog was clicked.
173    private boolean mDialogClicked;
174    private Dialog mEnableDialog;
175    private Dialog mAdbDialog;
176
177    @Override
178    public void onCreate(Bundle icicle) {
179        super.onCreate(icicle);
180
181        mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
182        mBackupManager = IBackupManager.Stub.asInterface(
183                ServiceManager.getService(Context.BACKUP_SERVICE));
184        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
185
186        addPreferencesFromResource(R.xml.development_prefs);
187
188        mEnableAdb = findAndInitCheckboxPref(ENABLE_ADB);
189        mBugreportInPower = findAndInitCheckboxPref(BUGREPORT_IN_POWER_KEY);
190        mKeepScreenOn = findAndInitCheckboxPref(KEEP_SCREEN_ON);
191        mEnforceReadExternal = findAndInitCheckboxPref(ENFORCE_READ_EXTERNAL);
192        mAllowMockLocation = findAndInitCheckboxPref(ALLOW_MOCK_LOCATION);
193        mPassword = (PreferenceScreen) findPreference(LOCAL_BACKUP_PASSWORD);
194        mAllPrefs.add(mPassword);
195
196        mDebugAppPref = findPreference(DEBUG_APP_KEY);
197        mAllPrefs.add(mDebugAppPref);
198        mWaitForDebugger = findAndInitCheckboxPref(WAIT_FOR_DEBUGGER_KEY);
199        mVerifyAppsOverUsb = findAndInitCheckboxPref(VERIFY_APPS_OVER_USB_KEY);
200        if (!showVerifierSetting()) {
201            PreferenceGroup debugDebuggingCategory = (PreferenceGroup)
202                    findPreference(DEBUG_DEBUGGING_CATEGORY_KEY);
203            if (debugDebuggingCategory != null) {
204                debugDebuggingCategory.removePreference(mVerifyAppsOverUsb);
205            } else {
206                mVerifyAppsOverUsb.setEnabled(false);
207            }
208        }
209        mStrictMode = findAndInitCheckboxPref(STRICT_MODE_KEY);
210        mPointerLocation = findAndInitCheckboxPref(POINTER_LOCATION_KEY);
211        mShowTouches = findAndInitCheckboxPref(SHOW_TOUCHES_KEY);
212        mShowScreenUpdates = findAndInitCheckboxPref(SHOW_SCREEN_UPDATES_KEY);
213        mDisableOverlays = findAndInitCheckboxPref(DISABLE_OVERLAYS_KEY);
214        mShowCpuUsage = findAndInitCheckboxPref(SHOW_CPU_USAGE_KEY);
215        mForceHardwareUi = findAndInitCheckboxPref(FORCE_HARDWARE_UI_KEY);
216        mTrackFrameTime = findAndInitCheckboxPref(TRACK_FRAME_TIME_KEY);
217        mShowHwScreenUpdates = findAndInitCheckboxPref(SHOW_HW_SCREEN_UPDATES_KEY);
218        mShowHwLayersUpdates = findAndInitCheckboxPref(SHOW_HW_LAYERS_UPDATES_KEY);
219        mShowHwOverdraw = findAndInitCheckboxPref(SHOW_HW_OVERDRAW_KEY);
220        mDebugLayout = findAndInitCheckboxPref(DEBUG_LAYOUT_KEY);
221        mWindowAnimationScale = (ListPreference) findPreference(WINDOW_ANIMATION_SCALE_KEY);
222        mAllPrefs.add(mWindowAnimationScale);
223        mWindowAnimationScale.setOnPreferenceChangeListener(this);
224        mTransitionAnimationScale = (ListPreference) findPreference(TRANSITION_ANIMATION_SCALE_KEY);
225        mAllPrefs.add(mTransitionAnimationScale);
226        mTransitionAnimationScale.setOnPreferenceChangeListener(this);
227        mAnimatorDurationScale = (ListPreference) findPreference(ANIMATOR_DURATION_SCALE_KEY);
228        mAllPrefs.add(mAnimatorDurationScale);
229        mAnimatorDurationScale.setOnPreferenceChangeListener(this);
230        mOverlayDisplayDevices = (ListPreference) findPreference(OVERLAY_DISPLAY_DEVICES_KEY);
231        mAllPrefs.add(mOverlayDisplayDevices);
232        mOverlayDisplayDevices.setOnPreferenceChangeListener(this);
233        mEnableTracesPref = (MultiCheckPreference)findPreference(ENABLE_TRACES_KEY);
234        String[] traceValues = new String[Trace.TRACE_TAGS.length];
235        for (int i=Trace.TRACE_FLAGS_START_BIT; i<traceValues.length; i++) {
236            traceValues[i] = Integer.toString(1<<i);
237        }
238        mEnableTracesPref.setEntries(Trace.TRACE_TAGS);
239        mEnableTracesPref.setEntryValues(traceValues);
240        mAllPrefs.add(mEnableTracesPref);
241        mEnableTracesPref.setOnPreferenceChangeListener(this);
242
243        mImmediatelyDestroyActivities = (CheckBoxPreference) findPreference(
244                IMMEDIATELY_DESTROY_ACTIVITIES_KEY);
245        mAllPrefs.add(mImmediatelyDestroyActivities);
246        mResetCbPrefs.add(mImmediatelyDestroyActivities);
247        mAppProcessLimit = (ListPreference) findPreference(APP_PROCESS_LIMIT_KEY);
248        mAllPrefs.add(mAppProcessLimit);
249        mAppProcessLimit.setOnPreferenceChangeListener(this);
250
251        mShowAllANRs = (CheckBoxPreference) findPreference(
252                SHOW_ALL_ANRS_KEY);
253        mAllPrefs.add(mShowAllANRs);
254        mResetCbPrefs.add(mShowAllANRs);
255
256        Preference hdcpChecking = findPreference(HDCP_CHECKING_KEY);
257        if (hdcpChecking != null) {
258            mAllPrefs.add(hdcpChecking);
259        }
260        removeHdcpOptionsForProduction();
261    }
262
263    private CheckBoxPreference findAndInitCheckboxPref(String key) {
264        CheckBoxPreference pref = (CheckBoxPreference) findPreference(key);
265        if (pref == null) {
266            throw new IllegalArgumentException("Cannot find preference with key = " + key);
267        }
268        mAllPrefs.add(pref);
269        mResetCbPrefs.add(pref);
270        return pref;
271    }
272
273    @Override
274    public void onActivityCreated(Bundle savedInstanceState) {
275        super.onActivityCreated(savedInstanceState);
276
277        final Activity activity = getActivity();
278        mEnabledSwitch = new Switch(activity);
279
280        final int padding = activity.getResources().getDimensionPixelSize(
281                R.dimen.action_bar_switch_padding);
282        mEnabledSwitch.setPadding(0, 0, padding, 0);
283        mEnabledSwitch.setOnCheckedChangeListener(this);
284    }
285
286    @Override
287    public void onStart() {
288        super.onStart();
289        final Activity activity = getActivity();
290        activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
291                ActionBar.DISPLAY_SHOW_CUSTOM);
292        activity.getActionBar().setCustomView(mEnabledSwitch, new ActionBar.LayoutParams(
293                ActionBar.LayoutParams.WRAP_CONTENT,
294                ActionBar.LayoutParams.WRAP_CONTENT,
295                Gravity.CENTER_VERTICAL | Gravity.END));
296    }
297
298    @Override
299    public void onStop() {
300        super.onStop();
301        final Activity activity = getActivity();
302        activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM);
303        activity.getActionBar().setCustomView(null);
304    }
305
306    private void removeHdcpOptionsForProduction() {
307        if ("user".equals(Build.TYPE)) {
308            Preference hdcpChecking = findPreference(HDCP_CHECKING_KEY);
309            if (hdcpChecking != null) {
310                // Remove the preference
311                getPreferenceScreen().removePreference(hdcpChecking);
312                mAllPrefs.remove(hdcpChecking);
313            }
314        }
315    }
316
317    private void setPrefsEnabledState(boolean enabled) {
318        for (int i = 0; i < mAllPrefs.size(); i++) {
319            Preference pref = mAllPrefs.get(i);
320            pref.setEnabled(enabled && !mDisabledPrefs.contains(pref));
321        }
322        updateAllOptions();
323    }
324
325    @Override
326    public void onResume() {
327        super.onResume();
328
329        if (mDpm.getMaximumTimeToLock(null) > 0) {
330            // A DeviceAdmin has specified a maximum time until the device
331            // will lock...  in this case we can't allow the user to turn
332            // on "stay awake when plugged in" because that would defeat the
333            // restriction.
334            mDisabledPrefs.add(mKeepScreenOn);
335        } else {
336            mDisabledPrefs.remove(mKeepScreenOn);
337        }
338
339        final ContentResolver cr = getActivity().getContentResolver();
340        mLastEnabledState = Settings.Global.getInt(cr,
341                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
342        mEnabledSwitch.setChecked(mLastEnabledState);
343        setPrefsEnabledState(mLastEnabledState);
344
345        if (mHaveDebugSettings && !mLastEnabledState) {
346            // Overall debugging is disabled, but there are some debug
347            // settings that are enabled.  This is an invalid state.  Switch
348            // to debug settings being enabled, so the user knows there is
349            // stuff enabled and can turn it all off if they want.
350            Settings.Global.putInt(getActivity().getContentResolver(),
351                    Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
352            mLastEnabledState = true;
353            setPrefsEnabledState(mLastEnabledState);
354        }
355    }
356
357    void updateCheckBox(CheckBoxPreference checkBox, boolean value) {
358        checkBox.setChecked(value);
359        mHaveDebugSettings |= value;
360    }
361
362    private void updateAllOptions() {
363        final Context context = getActivity();
364        final ContentResolver cr = context.getContentResolver();
365        mHaveDebugSettings = false;
366        updateCheckBox(mEnableAdb, Settings.Global.getInt(cr,
367                Settings.Global.ADB_ENABLED, 0) != 0);
368        updateCheckBox(mBugreportInPower, Settings.Secure.getInt(cr,
369                Settings.Secure.BUGREPORT_IN_POWER_MENU, 0) != 0);
370        updateCheckBox(mKeepScreenOn, Settings.Global.getInt(cr,
371                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0) != 0);
372        updateCheckBox(mEnforceReadExternal, isPermissionEnforced(READ_EXTERNAL_STORAGE));
373        updateCheckBox(mAllowMockLocation, Settings.Secure.getInt(cr,
374                Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0);
375        updateHdcpValues();
376        updatePasswordSummary();
377        updateDebuggerOptions();
378        updateStrictModeVisualOptions();
379        updatePointerLocationOptions();
380        updateShowTouchesOptions();
381        updateFlingerOptions();
382        updateCpuUsageOptions();
383        updateHardwareUiOptions();
384        updateTrackFrameTimeOptions();
385        updateShowHwScreenUpdatesOptions();
386        updateShowHwLayersUpdatesOptions();
387        updateShowHwOverdrawOptions();
388        updateDebugLayoutOptions();
389        updateAnimationScaleOptions();
390        updateOverlayDisplayDevicesOptions();
391        updateEnableTracesOptions();
392        updateImmediatelyDestroyActivitiesOptions();
393        updateAppProcessLimitOptions();
394        updateShowAllANRsOptions();
395        updateVerifyAppsOverUsbOptions();
396    }
397
398    private void resetDangerousOptions() {
399        mDontPokeProperties = true;
400        for (int i=0; i<mResetCbPrefs.size(); i++) {
401            CheckBoxPreference cb = mResetCbPrefs.get(i);
402            if (cb.isChecked()) {
403                cb.setChecked(false);
404                onPreferenceTreeClick(null, cb);
405            }
406        }
407        resetDebuggerOptions();
408        writeAnimationScaleOption(0, mWindowAnimationScale, null);
409        writeAnimationScaleOption(1, mTransitionAnimationScale, null);
410        writeAnimationScaleOption(2, mAnimatorDurationScale, null);
411        writeOverlayDisplayDevicesOptions(null);
412        writeEnableTracesOptions(0);
413        writeAppProcessLimitOptions(null);
414        mHaveDebugSettings = false;
415        updateAllOptions();
416        mDontPokeProperties = false;
417        pokeSystemProperties();
418    }
419
420    private void updateHdcpValues() {
421        int index = 1; // Defaults to drm-only. Needs to match with R.array.hdcp_checking_values
422        ListPreference hdcpChecking = (ListPreference) findPreference(HDCP_CHECKING_KEY);
423        if (hdcpChecking != null) {
424            String currentValue = SystemProperties.get(HDCP_CHECKING_PROPERTY);
425            String[] values = getResources().getStringArray(R.array.hdcp_checking_values);
426            String[] summaries = getResources().getStringArray(R.array.hdcp_checking_summaries);
427            for (int i = 0; i < values.length; i++) {
428                if (currentValue.equals(values[i])) {
429                    index = i;
430                    break;
431                }
432            }
433            hdcpChecking.setValue(values[index]);
434            hdcpChecking.setSummary(summaries[index]);
435            hdcpChecking.setOnPreferenceChangeListener(this);
436        }
437    }
438
439    private void updatePasswordSummary() {
440        try {
441            if (mBackupManager.hasBackupPassword()) {
442                mPassword.setSummary(R.string.local_backup_password_summary_change);
443            } else {
444                mPassword.setSummary(R.string.local_backup_password_summary_none);
445            }
446        } catch (RemoteException e) {
447            // Not much we can do here
448        }
449    }
450
451    private void writeDebuggerOptions() {
452        try {
453            ActivityManagerNative.getDefault().setDebugApp(
454                mDebugApp, mWaitForDebugger.isChecked(), true);
455        } catch (RemoteException ex) {
456        }
457    }
458
459    private static void resetDebuggerOptions() {
460        try {
461            ActivityManagerNative.getDefault().setDebugApp(
462                    null, false, true);
463        } catch (RemoteException ex) {
464        }
465    }
466
467    private void updateDebuggerOptions() {
468        mDebugApp = Settings.System.getString(
469                getActivity().getContentResolver(), Settings.System.DEBUG_APP);
470        updateCheckBox(mWaitForDebugger, Settings.System.getInt(
471                getActivity().getContentResolver(), Settings.System.WAIT_FOR_DEBUGGER, 0) != 0);
472        if (mDebugApp != null && mDebugApp.length() > 0) {
473            String label;
474            try {
475                ApplicationInfo ai = getActivity().getPackageManager().getApplicationInfo(mDebugApp,
476                        PackageManager.GET_DISABLED_COMPONENTS);
477                CharSequence lab = getActivity().getPackageManager().getApplicationLabel(ai);
478                label = lab != null ? lab.toString() : mDebugApp;
479            } catch (PackageManager.NameNotFoundException e) {
480                label = mDebugApp;
481            }
482            mDebugAppPref.setSummary(getResources().getString(R.string.debug_app_set, label));
483            mWaitForDebugger.setEnabled(true);
484            mHaveDebugSettings = true;
485        } else {
486            mDebugAppPref.setSummary(getResources().getString(R.string.debug_app_not_set));
487            mWaitForDebugger.setEnabled(false);
488        }
489    }
490
491    private void updateVerifyAppsOverUsbOptions() {
492        updateCheckBox(mVerifyAppsOverUsb, Settings.Global.getInt(getActivity().getContentResolver(),
493                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0);
494        mVerifyAppsOverUsb.setEnabled(enableVerifierSetting());
495    }
496
497    private void writeVerifyAppsOverUsbOptions() {
498        Settings.Global.putInt(getActivity().getContentResolver(),
499              Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, mVerifyAppsOverUsb.isChecked() ? 1 : 0);
500    }
501
502    private boolean enableVerifierSetting() {
503        final ContentResolver cr = getActivity().getContentResolver();
504        if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
505            return false;
506        }
507        if (Settings.Global.getInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 0) {
508            return false;
509        } else {
510            final PackageManager pm = getActivity().getPackageManager();
511            final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
512            verification.setType(PACKAGE_MIME_TYPE);
513            verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
514            final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);
515            if (receivers.size() == 0) {
516                return false;
517            }
518        }
519        return true;
520    }
521
522    private boolean showVerifierSetting() {
523        return Settings.Global.getInt(getActivity().getContentResolver(),
524                Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0;
525    }
526
527    // Returns the current state of the system property that controls
528    // strictmode flashes.  One of:
529    //    0: not explicitly set one way or another
530    //    1: on
531    //    2: off
532    private static int currentStrictModeActiveIndex() {
533        if (TextUtils.isEmpty(SystemProperties.get(StrictMode.VISUAL_PROPERTY))) {
534            return 0;
535        }
536        boolean enabled = SystemProperties.getBoolean(StrictMode.VISUAL_PROPERTY, false);
537        return enabled ? 1 : 2;
538    }
539
540    private void writeStrictModeVisualOptions() {
541        try {
542            mWindowManager.setStrictModeVisualIndicatorPreference(mStrictMode.isChecked()
543                    ? "1" : "");
544        } catch (RemoteException e) {
545        }
546    }
547
548    private void updateStrictModeVisualOptions() {
549        updateCheckBox(mStrictMode, currentStrictModeActiveIndex() == 1);
550    }
551
552    private void writePointerLocationOptions() {
553        Settings.System.putInt(getActivity().getContentResolver(),
554                Settings.System.POINTER_LOCATION, mPointerLocation.isChecked() ? 1 : 0);
555    }
556
557    private void updatePointerLocationOptions() {
558        updateCheckBox(mPointerLocation, Settings.System.getInt(getActivity().getContentResolver(),
559                Settings.System.POINTER_LOCATION, 0) != 0);
560    }
561
562    private void writeShowTouchesOptions() {
563        Settings.System.putInt(getActivity().getContentResolver(),
564                Settings.System.SHOW_TOUCHES, mShowTouches.isChecked() ? 1 : 0);
565    }
566
567    private void updateShowTouchesOptions() {
568        updateCheckBox(mShowTouches, Settings.System.getInt(getActivity().getContentResolver(),
569                Settings.System.SHOW_TOUCHES, 0) != 0);
570    }
571
572    private void updateFlingerOptions() {
573        // magic communication with surface flinger.
574        try {
575            IBinder flinger = ServiceManager.getService("SurfaceFlinger");
576            if (flinger != null) {
577                Parcel data = Parcel.obtain();
578                Parcel reply = Parcel.obtain();
579                data.writeInterfaceToken("android.ui.ISurfaceComposer");
580                flinger.transact(1010, data, reply, 0);
581                @SuppressWarnings("unused")
582                int showCpu = reply.readInt();
583                @SuppressWarnings("unused")
584                int enableGL = reply.readInt();
585                int showUpdates = reply.readInt();
586                updateCheckBox(mShowScreenUpdates, showUpdates != 0);
587                @SuppressWarnings("unused")
588                int showBackground = reply.readInt();
589                int disableOverlays = reply.readInt();
590                updateCheckBox(mDisableOverlays, disableOverlays != 0);
591                reply.recycle();
592                data.recycle();
593            }
594        } catch (RemoteException ex) {
595        }
596    }
597
598    private void writeShowUpdatesOption() {
599        try {
600            IBinder flinger = ServiceManager.getService("SurfaceFlinger");
601            if (flinger != null) {
602                Parcel data = Parcel.obtain();
603                data.writeInterfaceToken("android.ui.ISurfaceComposer");
604                final int showUpdates = mShowScreenUpdates.isChecked() ? 1 : 0;
605                data.writeInt(showUpdates);
606                flinger.transact(1002, data, null, 0);
607                data.recycle();
608
609                updateFlingerOptions();
610            }
611        } catch (RemoteException ex) {
612        }
613    }
614
615    private void writeDisableOverlaysOption() {
616        try {
617            IBinder flinger = ServiceManager.getService("SurfaceFlinger");
618            if (flinger != null) {
619                Parcel data = Parcel.obtain();
620                data.writeInterfaceToken("android.ui.ISurfaceComposer");
621                final int disableOverlays = mDisableOverlays.isChecked() ? 1 : 0;
622                data.writeInt(disableOverlays);
623                flinger.transact(1008, data, null, 0);
624                data.recycle();
625
626                updateFlingerOptions();
627            }
628        } catch (RemoteException ex) {
629        }
630    }
631
632    private void updateHardwareUiOptions() {
633        updateCheckBox(mForceHardwareUi, SystemProperties.getBoolean(HARDWARE_UI_PROPERTY, false));
634    }
635
636    private void writeHardwareUiOptions() {
637        SystemProperties.set(HARDWARE_UI_PROPERTY, mForceHardwareUi.isChecked() ? "true" : "false");
638        pokeSystemProperties();
639    }
640
641    private void updateTrackFrameTimeOptions() {
642        updateCheckBox(mTrackFrameTime,
643                SystemProperties.getBoolean(HardwareRenderer.PROFILE_PROPERTY, false));
644    }
645
646    private void writeTrackFrameTimeOptions() {
647        SystemProperties.set(HardwareRenderer.PROFILE_PROPERTY,
648                mTrackFrameTime.isChecked() ? "true" : "false");
649        pokeSystemProperties();
650    }
651
652    private void updateShowHwScreenUpdatesOptions() {
653        updateCheckBox(mShowHwScreenUpdates,
654                SystemProperties.getBoolean(HardwareRenderer.DEBUG_DIRTY_REGIONS_PROPERTY, false));
655    }
656
657    private void writeShowHwScreenUpdatesOptions() {
658        SystemProperties.set(HardwareRenderer.DEBUG_DIRTY_REGIONS_PROPERTY,
659                mShowHwScreenUpdates.isChecked() ? "true" : null);
660        pokeSystemProperties();
661    }
662
663    private void updateShowHwLayersUpdatesOptions() {
664        updateCheckBox(mShowHwLayersUpdates, SystemProperties.getBoolean(
665                HardwareRenderer.DEBUG_SHOW_LAYERS_UPDATES_PROPERTY, false));
666    }
667
668    private void writeShowHwLayersUpdatesOptions() {
669        SystemProperties.set(HardwareRenderer.DEBUG_SHOW_LAYERS_UPDATES_PROPERTY,
670                mShowHwLayersUpdates.isChecked() ? "true" : null);
671        pokeSystemProperties();
672    }
673
674    private void updateShowHwOverdrawOptions() {
675        updateCheckBox(mShowHwOverdraw, SystemProperties.getBoolean(
676                HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false));
677    }
678
679    private void writeShowHwOverdrawOptions() {
680        SystemProperties.set(HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY,
681                mShowHwOverdraw.isChecked() ? "true" : null);
682        pokeSystemProperties();
683    }
684
685    private void updateDebugLayoutOptions() {
686        updateCheckBox(mDebugLayout,
687                SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false));
688    }
689
690    private void writeDebugLayoutOptions() {
691        SystemProperties.set(View.DEBUG_LAYOUT_PROPERTY,
692                mDebugLayout.isChecked() ? "true" : "false");
693        pokeSystemProperties();
694    }
695
696    private void updateCpuUsageOptions() {
697        updateCheckBox(mShowCpuUsage, Settings.System.getInt(getActivity().getContentResolver(),
698                Settings.System.SHOW_PROCESSES, 0) != 0);
699    }
700
701    private void writeCpuUsageOptions() {
702        boolean value = mShowCpuUsage.isChecked();
703        Settings.System.putInt(getActivity().getContentResolver(),
704                Settings.System.SHOW_PROCESSES, value ? 1 : 0);
705        Intent service = (new Intent())
706                .setClassName("com.android.systemui", "com.android.systemui.LoadAverageService");
707        if (value) {
708            getActivity().startService(service);
709        } else {
710            getActivity().stopService(service);
711        }
712    }
713
714    private void writeImmediatelyDestroyActivitiesOptions() {
715        try {
716            ActivityManagerNative.getDefault().setAlwaysFinish(
717                    mImmediatelyDestroyActivities.isChecked());
718        } catch (RemoteException ex) {
719        }
720    }
721
722    private void updateImmediatelyDestroyActivitiesOptions() {
723        updateCheckBox(mImmediatelyDestroyActivities, Settings.System.getInt(
724            getActivity().getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0);
725    }
726
727    private void updateAnimationScaleValue(int which, ListPreference pref) {
728        try {
729            float scale = mWindowManager.getAnimationScale(which);
730            if (scale != 1) {
731                mHaveDebugSettings = true;
732            }
733            CharSequence[] values = pref.getEntryValues();
734            for (int i=0; i<values.length; i++) {
735                float val = Float.parseFloat(values[i].toString());
736                if (scale <= val) {
737                    pref.setValueIndex(i);
738                    pref.setSummary(pref.getEntries()[i]);
739                    return;
740                }
741            }
742            pref.setValueIndex(values.length-1);
743            pref.setSummary(pref.getEntries()[0]);
744        } catch (RemoteException e) {
745        }
746    }
747
748    private void updateAnimationScaleOptions() {
749        updateAnimationScaleValue(0, mWindowAnimationScale);
750        updateAnimationScaleValue(1, mTransitionAnimationScale);
751        updateAnimationScaleValue(2, mAnimatorDurationScale);
752    }
753
754    private void writeAnimationScaleOption(int which, ListPreference pref, Object newValue) {
755        try {
756            float scale = newValue != null ? Float.parseFloat(newValue.toString()) : 1;
757            mWindowManager.setAnimationScale(which, scale);
758            updateAnimationScaleValue(which, pref);
759        } catch (RemoteException e) {
760        }
761    }
762
763    private void updateOverlayDisplayDevicesOptions() {
764        String value = Settings.Global.getString(getActivity().getContentResolver(),
765                Settings.Global.OVERLAY_DISPLAY_DEVICES);
766        if (value == null) {
767            value = "";
768        }
769
770        CharSequence[] values = mOverlayDisplayDevices.getEntryValues();
771        for (int i = 0; i < values.length; i++) {
772            if (value.contentEquals(values[i])) {
773                mOverlayDisplayDevices.setValueIndex(i);
774                mOverlayDisplayDevices.setSummary(mOverlayDisplayDevices.getEntries()[i]);
775                return;
776            }
777        }
778        mOverlayDisplayDevices.setValueIndex(0);
779        mOverlayDisplayDevices.setSummary(mOverlayDisplayDevices.getEntries()[0]);
780    }
781
782    private void writeOverlayDisplayDevicesOptions(Object newValue) {
783        Settings.Global.putString(getActivity().getContentResolver(),
784                Settings.Global.OVERLAY_DISPLAY_DEVICES, (String)newValue);
785        updateOverlayDisplayDevicesOptions();
786    }
787
788    private void updateAppProcessLimitOptions() {
789        try {
790            int limit = ActivityManagerNative.getDefault().getProcessLimit();
791            CharSequence[] values = mAppProcessLimit.getEntryValues();
792            for (int i=0; i<values.length; i++) {
793                int val = Integer.parseInt(values[i].toString());
794                if (val >= limit) {
795                    if (i != 0) {
796                        mHaveDebugSettings = true;
797                    }
798                    mAppProcessLimit.setValueIndex(i);
799                    mAppProcessLimit.setSummary(mAppProcessLimit.getEntries()[i]);
800                    return;
801                }
802            }
803            mAppProcessLimit.setValueIndex(0);
804            mAppProcessLimit.setSummary(mAppProcessLimit.getEntries()[0]);
805        } catch (RemoteException e) {
806        }
807    }
808
809    private void writeAppProcessLimitOptions(Object newValue) {
810        try {
811            int limit = newValue != null ? Integer.parseInt(newValue.toString()) : -1;
812            ActivityManagerNative.getDefault().setProcessLimit(limit);
813            updateAppProcessLimitOptions();
814        } catch (RemoteException e) {
815        }
816    }
817
818    private void writeShowAllANRsOptions() {
819        Settings.Secure.putInt(getActivity().getContentResolver(),
820                Settings.Secure.ANR_SHOW_BACKGROUND,
821                mShowAllANRs.isChecked() ? 1 : 0);
822    }
823
824    private void updateShowAllANRsOptions() {
825        updateCheckBox(mShowAllANRs, Settings.Secure.getInt(
826            getActivity().getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0);
827    }
828
829    private void updateEnableTracesOptions() {
830        long flags = SystemProperties.getLong(Trace.PROPERTY_TRACE_TAG_ENABLEFLAGS, 0);
831        String[] values = mEnableTracesPref.getEntryValues();
832        int numSet = 0;
833        for (int i=Trace.TRACE_FLAGS_START_BIT; i<values.length; i++) {
834            boolean set = (flags&(1<<i)) != 0;
835            mEnableTracesPref.setValue(i-Trace.TRACE_FLAGS_START_BIT, set);
836            if (set) {
837                numSet++;
838            }
839        }
840        if (numSet == 0) {
841            mEnableTracesPref.setSummary(R.string.enable_traces_summary_none);
842        } else if (numSet == values.length) {
843            mHaveDebugSettings = true;
844            mEnableTracesPref.setSummary(R.string.enable_traces_summary_all);
845        } else {
846            mHaveDebugSettings = true;
847            mEnableTracesPref.setSummary(getString(R.string.enable_traces_summary_num, numSet));
848        }
849    }
850
851    private void writeEnableTracesOptions() {
852        long value = 0;
853        String[] values = mEnableTracesPref.getEntryValues();
854        for (int i=Trace.TRACE_FLAGS_START_BIT; i<values.length; i++) {
855            if (mEnableTracesPref.getValue(i-Trace.TRACE_FLAGS_START_BIT)) {
856                value |= 1<<i;
857            }
858        }
859        writeEnableTracesOptions(value);
860        // Make sure summary is updated.
861        updateEnableTracesOptions();
862    }
863
864    private void writeEnableTracesOptions(long value) {
865        SystemProperties.set(Trace.PROPERTY_TRACE_TAG_ENABLEFLAGS,
866                "0x" + Long.toString(value, 16));
867        pokeSystemProperties();
868    }
869
870    @Override
871    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
872        if (buttonView == mEnabledSwitch) {
873            if (isChecked != mLastEnabledState) {
874                if (isChecked) {
875                    mDialogClicked = false;
876                    if (mEnableDialog != null) dismissDialogs();
877                    mEnableDialog = new AlertDialog.Builder(getActivity()).setMessage(
878                            getActivity().getResources().getString(
879                                    R.string.dev_settings_warning_message))
880                            .setTitle(R.string.dev_settings_warning_title)
881                            .setIconAttribute(android.R.attr.alertDialogIcon)
882                            .setPositiveButton(android.R.string.yes, this)
883                            .setNegativeButton(android.R.string.no, this)
884                            .show();
885                    mEnableDialog.setOnDismissListener(this);
886                } else {
887                    resetDangerousOptions();
888                    Settings.Global.putInt(getActivity().getContentResolver(),
889                            Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
890                    mLastEnabledState = isChecked;
891                    setPrefsEnabledState(mLastEnabledState);
892                }
893            }
894        }
895    }
896
897    @Override
898    public void onActivityResult(int requestCode, int resultCode, Intent data) {
899        if (requestCode == RESULT_DEBUG_APP) {
900            if (resultCode == Activity.RESULT_OK) {
901                mDebugApp = data.getAction();
902                writeDebuggerOptions();
903                updateDebuggerOptions();
904            }
905        } else {
906            super.onActivityResult(requestCode, resultCode, data);
907        }
908    }
909
910    @Override
911    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
912
913        if (Utils.isMonkeyRunning()) {
914            return false;
915        }
916
917        if (preference == mEnableAdb) {
918            if (mEnableAdb.isChecked()) {
919                mDialogClicked = false;
920                if (mAdbDialog != null) dismissDialogs();
921                mAdbDialog = new AlertDialog.Builder(getActivity()).setMessage(
922                        getActivity().getResources().getString(R.string.adb_warning_message))
923                        .setTitle(R.string.adb_warning_title)
924                        .setIconAttribute(android.R.attr.alertDialogIcon)
925                        .setPositiveButton(android.R.string.yes, this)
926                        .setNegativeButton(android.R.string.no, this)
927                        .show();
928                mAdbDialog.setOnDismissListener(this);
929            } else {
930                Settings.Global.putInt(getActivity().getContentResolver(),
931                        Settings.Global.ADB_ENABLED, 0);
932                mVerifyAppsOverUsb.setEnabled(false);
933                mVerifyAppsOverUsb.setChecked(false);
934            }
935        } else if (preference == mBugreportInPower) {
936            Settings.Secure.putInt(getActivity().getContentResolver(),
937                    Settings.Secure.BUGREPORT_IN_POWER_MENU,
938                    mBugreportInPower.isChecked() ? 1 : 0);
939        } else if (preference == mKeepScreenOn) {
940            Settings.Global.putInt(getActivity().getContentResolver(),
941                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
942                    mKeepScreenOn.isChecked() ?
943                    (BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
944        } else if (preference == mEnforceReadExternal) {
945            if (mEnforceReadExternal.isChecked()) {
946                ConfirmEnforceFragment.show(this);
947            } else {
948                setPermissionEnforced(getActivity(), READ_EXTERNAL_STORAGE, false);
949            }
950        } else if (preference == mAllowMockLocation) {
951            Settings.Secure.putInt(getActivity().getContentResolver(),
952                    Settings.Secure.ALLOW_MOCK_LOCATION,
953                    mAllowMockLocation.isChecked() ? 1 : 0);
954        } else if (preference == mDebugAppPref) {
955            startActivityForResult(new Intent(getActivity(), AppPicker.class), RESULT_DEBUG_APP);
956        } else if (preference == mWaitForDebugger) {
957            writeDebuggerOptions();
958        } else if (preference == mVerifyAppsOverUsb) {
959            writeVerifyAppsOverUsbOptions();
960        } else if (preference == mStrictMode) {
961            writeStrictModeVisualOptions();
962        } else if (preference == mPointerLocation) {
963            writePointerLocationOptions();
964        } else if (preference == mShowTouches) {
965            writeShowTouchesOptions();
966        } else if (preference == mShowScreenUpdates) {
967            writeShowUpdatesOption();
968        } else if (preference == mDisableOverlays) {
969            writeDisableOverlaysOption();
970        } else if (preference == mShowCpuUsage) {
971            writeCpuUsageOptions();
972        } else if (preference == mImmediatelyDestroyActivities) {
973            writeImmediatelyDestroyActivitiesOptions();
974        } else if (preference == mShowAllANRs) {
975            writeShowAllANRsOptions();
976        } else if (preference == mForceHardwareUi) {
977            writeHardwareUiOptions();
978        } else if (preference == mTrackFrameTime) {
979            writeTrackFrameTimeOptions();
980        } else if (preference == mShowHwScreenUpdates) {
981            writeShowHwScreenUpdatesOptions();
982        } else if (preference == mShowHwLayersUpdates) {
983            writeShowHwLayersUpdatesOptions();
984        } else if (preference == mShowHwOverdraw) {
985            writeShowHwOverdrawOptions();
986        } else if (preference == mDebugLayout) {
987            writeDebugLayoutOptions();
988        }
989
990        return false;
991    }
992
993    @Override
994    public boolean onPreferenceChange(Preference preference, Object newValue) {
995        if (HDCP_CHECKING_KEY.equals(preference.getKey())) {
996            SystemProperties.set(HDCP_CHECKING_PROPERTY, newValue.toString());
997            updateHdcpValues();
998            pokeSystemProperties();
999            return true;
1000        } else if (preference == mWindowAnimationScale) {
1001            writeAnimationScaleOption(0, mWindowAnimationScale, newValue);
1002            return true;
1003        } else if (preference == mTransitionAnimationScale) {
1004            writeAnimationScaleOption(1, mTransitionAnimationScale, newValue);
1005            return true;
1006        } else if (preference == mAnimatorDurationScale) {
1007            writeAnimationScaleOption(2, mAnimatorDurationScale, newValue);
1008            return true;
1009        } else if (preference == mOverlayDisplayDevices) {
1010            writeOverlayDisplayDevicesOptions(newValue);
1011            return true;
1012        } else if (preference == mEnableTracesPref) {
1013            writeEnableTracesOptions();
1014            return true;
1015        } else if (preference == mAppProcessLimit) {
1016            writeAppProcessLimitOptions(newValue);
1017            return true;
1018        }
1019        return false;
1020    }
1021
1022    private void dismissDialogs() {
1023        if (mAdbDialog != null) {
1024            mAdbDialog.dismiss();
1025            mAdbDialog = null;
1026        }
1027        if (mEnableDialog != null) {
1028            mEnableDialog.dismiss();
1029            mEnableDialog = null;
1030        }
1031    }
1032
1033    public void onClick(DialogInterface dialog, int which) {
1034        if (dialog == mAdbDialog) {
1035            if (which == DialogInterface.BUTTON_POSITIVE) {
1036                mDialogClicked = true;
1037                Settings.Global.putInt(getActivity().getContentResolver(),
1038                        Settings.Global.ADB_ENABLED, 1);
1039                mVerifyAppsOverUsb.setEnabled(true);
1040                updateVerifyAppsOverUsbOptions();
1041            } else {
1042                // Reset the toggle
1043                mEnableAdb.setChecked(false);
1044            }
1045        } else if (dialog == mEnableDialog) {
1046            if (which == DialogInterface.BUTTON_POSITIVE) {
1047                mDialogClicked = true;
1048                Settings.Global.putInt(getActivity().getContentResolver(),
1049                        Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
1050                mLastEnabledState = true;
1051                setPrefsEnabledState(mLastEnabledState);
1052            } else {
1053                // Reset the toggle
1054                mEnabledSwitch.setChecked(false);
1055            }
1056        }
1057    }
1058
1059    public void onDismiss(DialogInterface dialog) {
1060        // Assuming that onClick gets called first
1061        if (dialog == mAdbDialog) {
1062            if (!mDialogClicked) {
1063                mEnableAdb.setChecked(false);
1064            }
1065            mAdbDialog = null;
1066        } else if (dialog == mEnableDialog) {
1067            if (!mDialogClicked) {
1068                mEnabledSwitch.setChecked(false);
1069            }
1070            mEnableDialog = null;
1071        }
1072    }
1073
1074    @Override
1075    public void onDestroy() {
1076        dismissDialogs();
1077        super.onDestroy();
1078    }
1079
1080    void pokeSystemProperties() {
1081        if (!mDontPokeProperties) {
1082            //noinspection unchecked
1083            (new SystemPropPoker()).execute();
1084        }
1085    }
1086
1087    static class SystemPropPoker extends AsyncTask<Void, Void, Void> {
1088        @Override
1089        protected Void doInBackground(Void... params) {
1090            String[] services;
1091            try {
1092                services = ServiceManager.listServices();
1093            } catch (RemoteException e) {
1094                return null;
1095            }
1096            for (String service : services) {
1097                IBinder obj = ServiceManager.checkService(service);
1098                if (obj != null) {
1099                    Parcel data = Parcel.obtain();
1100                    try {
1101                        obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
1102                    } catch (RemoteException e) {
1103                    }
1104                    data.recycle();
1105                }
1106            }
1107            return null;
1108        }
1109    }
1110
1111    /**
1112     * Dialog to confirm enforcement of {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
1113     */
1114    public static class ConfirmEnforceFragment extends DialogFragment {
1115        public static void show(DevelopmentSettings parent) {
1116            final ConfirmEnforceFragment dialog = new ConfirmEnforceFragment();
1117            dialog.setTargetFragment(parent, 0);
1118            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ENFORCE);
1119        }
1120
1121        @Override
1122        public Dialog onCreateDialog(Bundle savedInstanceState) {
1123            final Context context = getActivity();
1124
1125            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1126            builder.setTitle(R.string.enforce_read_external_confirm_title);
1127            builder.setMessage(R.string.enforce_read_external_confirm_message);
1128
1129            builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
1130                @Override
1131                public void onClick(DialogInterface dialog, int which) {
1132                    setPermissionEnforced(context, READ_EXTERNAL_STORAGE, true);
1133                    ((DevelopmentSettings) getTargetFragment()).updateAllOptions();
1134                }
1135            });
1136            builder.setNegativeButton(android.R.string.cancel, new OnClickListener() {
1137                @Override
1138                public void onClick(DialogInterface dialog, int which) {
1139                    ((DevelopmentSettings) getTargetFragment()).updateAllOptions();
1140                }
1141            });
1142
1143            return builder.create();
1144        }
1145    }
1146
1147    private static boolean isPermissionEnforced(String permission) {
1148        try {
1149            return ActivityThread.getPackageManager().isPermissionEnforced(permission);
1150        } catch (RemoteException e) {
1151            throw new RuntimeException("Problem talking with PackageManager", e);
1152        }
1153    }
1154
1155    private static void setPermissionEnforced(
1156            Context context, String permission, boolean enforced) {
1157        try {
1158            // TODO: offload to background thread
1159            ActivityThread.getPackageManager()
1160                    .setPermissionEnforced(READ_EXTERNAL_STORAGE, enforced);
1161        } catch (RemoteException e) {
1162            throw new RuntimeException("Problem talking with PackageManager", e);
1163        }
1164    }
1165}
1166