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