Merge "Introduce HdcpCheckingPreferenceController"
diff --git a/src/com/android/settings/applications/DrawOverlayDetails.java b/src/com/android/settings/applications/DrawOverlayDetails.java
index c6f3cc0..78f1c08 100644
--- a/src/com/android/settings/applications/DrawOverlayDetails.java
+++ b/src/com/android/settings/applications/DrawOverlayDetails.java
@@ -29,12 +29,13 @@
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.util.Log;
 
+import android.view.Window;
+import android.view.WindowManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
 import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
-import com.android.settings.core.TouchOverlayManager;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 
@@ -60,8 +61,6 @@
     private Intent mSettingsIntent;
     private OverlayState mOverlayState;
 
-    private TouchOverlayManager mTouchOverlayManager;
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -69,7 +68,6 @@
         Context context = getActivity();
         mOverlayBridge = new AppStateOverlayBridge(context, mState, null);
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mTouchOverlayManager = new TouchOverlayManager(context);
 
         // find preferences
         addPreferencesFromResource(R.xml.app_ops_permissions_details);
@@ -92,17 +90,17 @@
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
-
-        mTouchOverlayManager.setOverlayAllowed(false);
+    public void onResume() {
+        super.onResume();
+        getActivity().getWindow().addFlags(
+                WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
     }
 
     @Override
-    public void onStop() {
-        super.onStop();
-
-        mTouchOverlayManager.setOverlayAllowed(true);
+    public void onPause() {
+        getActivity().getWindow().clearFlags(
+                WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        super.onPause();
     }
 
     @Override
@@ -153,16 +151,6 @@
                 .getMetricsFeatureProvider().action(getContext(), logCategory, packageName);
     }
 
-    private boolean canDrawOverlay(String pkgName) {
-        int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                mPackageInfo.applicationInfo.uid, pkgName);
-        if (result == AppOpsManager.MODE_ALLOWED) {
-            return true;
-        }
-
-        return false;
-    }
-
     @Override
     protected boolean refreshUi() {
         mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName,
diff --git a/src/com/android/settings/core/TouchOverlayManager.java b/src/com/android/settings/core/TouchOverlayManager.java
deleted file mode 100644
index f69d1bf..0000000
--- a/src/com/android/settings/core/TouchOverlayManager.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settings.core;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
-
-public class TouchOverlayManager {
-
-    private final Context mContext;
-    private final IBinder mToken = new Binder();
-
-    public TouchOverlayManager(Context context) {
-        mContext = context;
-    }
-
-    public void setOverlayAllowed(boolean allowed) {
-        final AppOpsManager aom = mContext.getSystemService(AppOpsManager.class);
-        if (aom != null) {
-            aom.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, !allowed, mToken);
-            aom.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, !allowed, mToken);
-        }
-    }
-}
diff --git a/src/com/android/settings/development/BugReportPreferenceController.java b/src/com/android/settings/development/BugReportPreferenceController.java
index 015071f..c05dd26 100644
--- a/src/com/android/settings/development/BugReportPreferenceController.java
+++ b/src/com/android/settings/development/BugReportPreferenceController.java
@@ -24,6 +24,10 @@
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
+/**
+ * deprecated in favor of {@link BugReportPreferenceControllerV2}
+ */
+@Deprecated
 public class BugReportPreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin {
 
diff --git a/src/com/android/settings/development/BugReportPreferenceControllerV2.java b/src/com/android/settings/development/BugReportPreferenceControllerV2.java
new file mode 100644
index 0000000..7df23a6
--- /dev/null
+++ b/src/com/android/settings/development/BugReportPreferenceControllerV2.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.os.UserManager;
+
+public class BugReportPreferenceControllerV2 extends DeveloperOptionsPreferenceController {
+
+    private static final String KEY_BUGREPORT = "bugreport";
+
+    private final UserManager mUserManager;
+
+    public BugReportPreferenceControllerV2(Context context) {
+        super(context);
+
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BUGREPORT;
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        // intentional no-op
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        // intentional no-op
+    }
+}
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
index 667ae31..b7b2759 100644
--- a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -23,4 +23,6 @@
     int REQUEST_CODE_ENABLE_OEM_UNLOCK = 0;
 
     int REQUEST_CODE_DEBUG_APP = 1;
+
+    int REQUEST_MOCK_LOCATION_APP = 2;
 }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 00a2375..a867330 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -245,14 +245,14 @@
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
             Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        // take bug report
+        controllers.add(new BugReportPreferenceControllerV2(context));
         controllers.add(new LocalBackupPasswordPreferenceController(context));
         controllers.add(new StayAwakePreferenceController(context, lifecycle));
         controllers.add(new HdcpCheckingPreferenceController(context));
         controllers.add(new BluetoothSnoopLogPreferenceController(context));
         controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
         // running services
-        // convert to file encryption
+        controllers.add(new FileEncryptionPreferenceController(context));
         controllers.add(new PictureColorModePreferenceController(context, lifecycle));
         controllers.add(new WebViewAppPreferenceControllerV2(context));
         controllers.add(new CoolColorTemperaturePreferenceController(context));
@@ -263,7 +263,7 @@
         controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
         controllers.add(new LocalTerminalPreferenceController(context));
         controllers.add(new BugReportInPowerPreferenceControllerV2(context));
-        // select mock location app
+        controllers.add(new MockLocationAppPreferenceController(context, fragment));
         controllers.add(new DebugViewAttributesPreferenceController(context));
         controllers.add(new SelectDebugAppPreferenceController(context, fragment));
         controllers.add(new WaitForDebuggerPreferenceController(context));
@@ -320,7 +320,7 @@
         // inactive apps
         controllers.add(new AllowAppsOnExternalPreferenceController(context));
         controllers.add(new ResizableActivityPreferenceController(context));
-        // reset shortcutmanager rate-limiting
+        controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
         return controllers;
     }
 
diff --git a/src/com/android/settings/development/FileEncryptionPreferenceController.java b/src/com/android/settings/development/FileEncryptionPreferenceController.java
new file mode 100644
index 0000000..463bb70
--- /dev/null
+++ b/src/com/android/settings/development/FileEncryptionPreferenceController.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.storage.IStorageManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+
+public class FileEncryptionPreferenceController extends DeveloperOptionsPreferenceController {
+
+    private static final String KEY_CONVERT_FBE = "convert_to_file_encryption";
+    private static final String KEY_STORAGE_MANAGER = "mount";
+
+    @VisibleForTesting
+    static final String FILE_ENCRYPTION_PROPERTY_KEY = "ro.crypto.type";
+
+    private final IStorageManager mStorageManager;
+
+    private Preference mPreference;
+
+    public FileEncryptionPreferenceController(Context context) {
+        super(context);
+
+        mStorageManager = getStorageManager();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (mStorageManager == null) {
+            return false;
+        }
+
+        try {
+            return mStorageManager.isConvertibleToFBE();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_CONVERT_FBE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (!TextUtils.equals("file",
+                SystemProperties.get(FILE_ENCRYPTION_PROPERTY_KEY, "none" /* default */))) {
+            return;
+        }
+
+        mPreference.setEnabled(false);
+        mPreference.setSummary(
+                mContext.getResources().getString(R.string.convert_to_file_encryption_done));
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        // intentional no-op
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        // intentional no-op
+    }
+
+    private IStorageManager getStorageManager() {
+        try {
+            return IStorageManager.Stub.asInterface(
+                    ServiceManager.getService(KEY_STORAGE_MANAGER));
+        } catch (VerifyError e) {
+            // Used for tests since Robolectric cannot initialize this class.
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/development/MockLocationAppPreferenceController.java b/src/com/android/settings/development/MockLocationAppPreferenceController.java
new file mode 100644
index 0000000..9f6c4d3
--- /dev/null
+++ b/src/com/android/settings/development/MockLocationAppPreferenceController.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+        .REQUEST_MOCK_LOCATION_APP;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import java.util.List;
+
+public class MockLocationAppPreferenceController extends DeveloperOptionsPreferenceController {
+
+    private static final String MOCK_LOCATION_APP_KEY = "mock_location_app";
+    private static final int[] MOCK_LOCATION_APP_OPS = new int[]{AppOpsManager.OP_MOCK_LOCATION};
+
+    private final DevelopmentSettingsDashboardFragment mFragment;
+    private final AppOpsManager mAppsOpsManager;
+    private final PackageManagerWrapper mPackageManager;
+    private Preference mPreference;
+
+    public MockLocationAppPreferenceController(Context context,
+            DevelopmentSettingsDashboardFragment fragment) {
+        super(context);
+
+        mFragment = fragment;
+        mAppsOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mPackageManager = new PackageManagerWrapper(context.getPackageManager());
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return MOCK_LOCATION_APP_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+            return false;
+        }
+        final Intent intent = new Intent(mContext, AppPicker.class);
+        intent.putExtra(AppPicker.EXTRA_REQUESTIING_PERMISSION,
+                Manifest.permission.ACCESS_MOCK_LOCATION);
+        mFragment.startActivityForResult(intent, REQUEST_MOCK_LOCATION_APP);
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        updateMockLocation();
+    }
+
+    @Override
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode != REQUEST_MOCK_LOCATION_APP || resultCode != Activity.RESULT_OK) {
+            return false;
+        }
+        writeMockLocation(data.getAction());
+        updateMockLocation();
+        return true;
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        mPreference.setEnabled(true);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        mPreference.setEnabled(false);
+    }
+
+    private void updateMockLocation() {
+        final String mockLocationApp = getCurrentMockLocationApp();
+
+        if (!TextUtils.isEmpty(mockLocationApp)) {
+            mPreference.setSummary(
+                    mContext.getResources().getString(R.string.mock_location_app_set,
+                            getAppLabel(mockLocationApp)));
+        } else {
+            mPreference.setSummary(
+                    mContext.getResources().getString(R.string.mock_location_app_not_set));
+        }
+    }
+
+    private void writeMockLocation(String mockLocationAppName) {
+        removeAllMockLocations();
+        // Enable the app op of the new mock location app if such.
+        if (!TextUtils.isEmpty(mockLocationAppName)) {
+            try {
+                final ApplicationInfo ai = mPackageManager.getApplicationInfo(
+                        mockLocationAppName, PackageManager.MATCH_DISABLED_COMPONENTS);
+                mAppsOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid,
+                        mockLocationAppName, AppOpsManager.MODE_ALLOWED);
+            } catch (PackageManager.NameNotFoundException e) {
+                /* ignore */
+            }
+        }
+    }
+
+    private String getAppLabel(String mockLocationApp) {
+        try {
+            final ApplicationInfo ai = mPackageManager.getApplicationInfo(
+                    mockLocationApp, PackageManager.MATCH_DISABLED_COMPONENTS);
+            final CharSequence appLabel = mPackageManager.getApplicationLabel(ai);
+            return appLabel != null ? appLabel.toString() : mockLocationApp;
+        } catch (PackageManager.NameNotFoundException e) {
+            return mockLocationApp;
+        }
+    }
+
+    private void removeAllMockLocations() {
+        // Disable the app op of the previous mock location app if such.
+        final List<AppOpsManager.PackageOps> packageOps = mAppsOpsManager.getPackagesForOps(
+                MOCK_LOCATION_APP_OPS);
+        if (packageOps == null) {
+            return;
+        }
+        // Should be one but in case we are in a bad state due to use of command line tools.
+        for (AppOpsManager.PackageOps packageOp : packageOps) {
+            if (packageOp.getOps().get(0).getMode() != AppOpsManager.MODE_ERRORED) {
+                removeMockLocationForApp(packageOp.getPackageName());
+            }
+        }
+    }
+
+    private void removeMockLocationForApp(String appName) {
+        try {
+            final ApplicationInfo ai = mPackageManager.getApplicationInfo(
+                    appName, PackageManager.MATCH_DISABLED_COMPONENTS);
+            mAppsOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid,
+                    appName, AppOpsManager.MODE_ERRORED);
+        } catch (PackageManager.NameNotFoundException e) {
+            /* ignore */
+        }
+    }
+
+    private String getCurrentMockLocationApp() {
+        final List<AppOpsManager.PackageOps> packageOps = mAppsOpsManager.getPackagesForOps(
+                MOCK_LOCATION_APP_OPS);
+        if (packageOps != null) {
+            for (AppOpsManager.PackageOps packageOp : packageOps) {
+                if (packageOp.getOps().get(0).getMode() == AppOpsManager.MODE_ALLOWED) {
+                    return packageOps.get(0).getPackageName();
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceController.java b/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceController.java
new file mode 100644
index 0000000..c8fdaec
--- /dev/null
+++ b/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.content.pm.IShortcutService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.settings.R;
+
+public class ShortcutManagerThrottlingPreferenceController extends
+        DeveloperOptionsPreferenceController {
+
+    private static final String TAG = "ShortcutMgrPrefCtrl";
+
+    private static final String SHORTCUT_MANAGER_RESET_KEY = "reset_shortcut_manager_throttling";
+
+    private final IShortcutService mShortcutService;
+
+    public ShortcutManagerThrottlingPreferenceController(Context context) {
+        super(context);
+
+        mShortcutService = getShortCutService();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return SHORTCUT_MANAGER_RESET_KEY;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!TextUtils.equals(SHORTCUT_MANAGER_RESET_KEY, preference.getKey())) {
+            return false;
+        }
+        resetShortcutManagerThrottling();
+        return true;
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchEnabled() {
+        // intentional no-op
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        // intentional no-op
+    }
+
+    private void resetShortcutManagerThrottling() {
+        if (mShortcutService == null) {
+            return;
+        }
+        try {
+            mShortcutService.resetThrottling();
+            Toast.makeText(mContext, R.string.reset_shortcut_manager_throttling_complete,
+                    Toast.LENGTH_SHORT).show();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to reset rate limiting", e);
+        }
+    }
+
+    private IShortcutService getShortCutService() {
+        try {
+            return IShortcutService.Stub.asInterface(
+                    ServiceManager.getService(Context.SHORTCUT_SERVICE));
+        } catch (VerifyError e) {
+            // Used for tests since Robolectric cannot initialize this class.
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
new file mode 100644
index 0000000..80851d3
--- /dev/null
+++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.featureflags;
+
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.util.FeatureFlagUtils;
+
+public class FeatureFlagPreference extends SwitchPreference {
+
+    private final String mKey;
+
+    public FeatureFlagPreference(Context context, String key) {
+        super(context);
+        mKey = key;
+        setKey(key);
+        setTitle(key);
+        setCheckedInternal(FeatureFlagUtils.isEnabled(mKey));
+    }
+
+    @Override
+    public void setChecked(boolean isChecked) {
+        setCheckedInternal(isChecked);
+        FeatureFlagUtils.setEnabled(mKey, isChecked);
+    }
+
+    private void setCheckedInternal(boolean isChecked) {
+        super.setChecked(isChecked);
+        setSummary(Boolean.toString(isChecked));
+    }
+}
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
index ee2258d..998e431 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
@@ -23,6 +23,7 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class FeatureFlagsDashboard extends DashboardFragment {
@@ -51,6 +52,8 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        return null;
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new FeatureFlagsPreferenceController(context, getLifecycle()));
+        return controllers;
     }
 }
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java
new file mode 100644
index 0000000..7c00591
--- /dev/null
+++ b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.featureflags;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+
+import java.util.Map;
+
+public class FeatureFlagsPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin, LifecycleObserver, OnStart {
+
+    private PreferenceScreen mScreen;
+
+    public FeatureFlagsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return null;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mScreen = screen;
+    }
+
+    @Override
+    public void onStart() {
+        if (mScreen == null) {
+            return;
+        }
+        final Map<String, String> featureMap = FeatureFlagUtils.getAllFeatureFlags();
+        if (featureMap == null) {
+            return;
+        }
+        mScreen.removeAll();
+        final Context prefContext = mScreen.getContext();
+        for (String prefixedFeature : featureMap.keySet()) {
+            if (prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_PREFIX)
+                    && !prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX)) {
+                final String feature = prefixedFeature.substring(
+                        FeatureFlagUtils.FFLAG_PREFIX.length());
+                final Preference pref = new FeatureFlagPreference(prefContext, feature);
+                mScreen.addPreference(pref);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/SerialNumberPreferenceController.java b/src/com/android/settings/deviceinfo/SerialNumberPreferenceController.java
index b69844d..31b905e 100644
--- a/src/com/android/settings/deviceinfo/SerialNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/SerialNumberPreferenceController.java
@@ -18,47 +18,19 @@
 
 import android.content.Context;
 import android.os.Build;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.deviceinfo.AbstractSerialNumberPreferenceController;
 
-public class SerialNumberPreferenceController extends AbstractPreferenceController implements
+/**
+ * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
+ */
+public class SerialNumberPreferenceController extends
+        AbstractSerialNumberPreferenceController implements
         PreferenceControllerMixin {
-
-    private static final String KEY_SERIAL_NUMBER = "serial_number";
-
-    private final String mSerialNumber;
-
     public SerialNumberPreferenceController(Context context) {
-        this(context, Build.getSerial());
-    }
-
-    @VisibleForTesting
-    SerialNumberPreferenceController(Context context, String serialNumber) {
         super(context);
-        mSerialNumber = serialNumber;
     }
 
-    @Override
-    public boolean isAvailable() {
-        return !TextUtils.isEmpty(mSerialNumber);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER);
-        if (pref != null) {
-            pref.setSummary(mSerialNumber);
-        }
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_SERIAL_NUMBER;
-    }
+    // This space intentionally left blank
 }
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
index 66e7c44..16d255b 100644
--- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -35,15 +35,13 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.SettingsStringUtil;
 import android.util.Slog;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
 import com.android.settings.R;
-import com.android.settings.core.TouchOverlayManager;
 
 /** @hide */
 public class NotificationAccessConfirmationActivity extends Activity
@@ -54,14 +52,12 @@
 
     private int mUserId;
     private ComponentName mComponentName;
-    private TouchOverlayManager mTouchOverlayManager;
     private NotificationManager mNm;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mTouchOverlayManager = new TouchOverlayManager(this);
         mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 
         mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
@@ -87,6 +83,20 @@
         getWindow().setCloseOnTouchOutside(false); 
     }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+        getWindow().addFlags(
+                WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+    }
+
+    @Override
+    public void onPause() {
+        getWindow().clearFlags(
+                WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        super.onPause();
+    }
+
     private void onAllow() {
         String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
         try {
@@ -130,16 +140,4 @@
             finish();
         }
     }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mTouchOverlayManager.setOverlayAllowed(false);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mTouchOverlayManager.setOverlayAllowed(true);
-    }
 }
diff --git a/src/com/android/settings/support/SupportDashboardActivity.java b/src/com/android/settings/support/SupportDashboardActivity.java
index 819d5f7..d3fcf9a 100644
--- a/src/com/android/settings/support/SupportDashboardActivity.java
+++ b/src/com/android/settings/support/SupportDashboardActivity.java
@@ -47,8 +47,8 @@
             supportFeatureProvider.startSupportV2(this);
         } else {
             startActivity(new Intent(this, LegacySupportActivity.class));
-            finish();
         }
+        finish();
     }
 
     /**
diff --git a/tests/robotests/src/android/util/FeatureFlagUtils.java b/tests/robotests/src/android/util/FeatureFlagUtils.java
index 6bc0557..500884a 100644
--- a/tests/robotests/src/android/util/FeatureFlagUtils.java
+++ b/tests/robotests/src/android/util/FeatureFlagUtils.java
@@ -19,6 +19,9 @@
 import android.os.SystemProperties;
 import android.text.TextUtils;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * This class is only needed to get around Robolectric issue.
  */
@@ -43,4 +46,19 @@
         value = SystemProperties.get(FFLAG_PREFIX + feature);
         return Boolean.parseBoolean(value);
     }
+
+    /**
+     * Override feature flag to new state.
+     */
+    public static void setEnabled(String feature, boolean enabled) {
+        SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
+    }
+
+
+    public static Map<String, String> getAllFeatureFlags() {
+        final Map<String, String> features = new HashMap<>();
+        features.put(FFLAG_PREFIX + "abc", "false");
+        features.put(FFLAG_OVERRIDE_PREFIX + "abc", "true");
+        return features;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java
index 6122576..5d20a4c 100644
--- a/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java
@@ -19,52 +19,59 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.app.Activity;
 import android.content.Context;
 
+import android.view.Window;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.core.TouchOverlayManager;
 import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.shadow.ShadowPreferenceFragment;
 
+import com.android.settings.testutils.shadow.ShadowAppInfoBase;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
+import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class DrawOverlayDetailsTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-
-    private FakeFeatureFactory mFeatureFactory;
-    private DrawOverlayDetails mFragment;
+    private Activity mActivity;
 
     @Mock
-    private TouchOverlayManager mTouchOverlayManager;
+    private Window mWindow;
+
+    private FakeFeatureFactory mFeatureFactory;
+
+    @Spy
+    private DrawOverlayDetails mFragment;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        FakeFeatureFactory.setupForTest(mContext);
-        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-
-        mFragment = new DrawOverlayDetails();
-        ReflectionHelpers.setField(mFragment, "mTouchOverlayManager", mTouchOverlayManager);
+        FakeFeatureFactory.setupForTest(mActivity);
+        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mActivity);
     }
 
     @Test
     public void logSpecialPermissionChange() {
-        mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
+        when(mFragment.getContext()).thenReturn(
+                ShadowApplication.getInstance().getApplicationContext());
+
         mFragment.logSpecialPermissionChange(true, "app");
         verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
                 eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW), eq("app"));
@@ -75,16 +82,17 @@
     }
 
     @Test
-    @Config(shadows = ShadowPreferenceFragment.class)
-    public void onStart_disableOverlay() {
-        mFragment.onStart();
-        verify(mTouchOverlayManager).setOverlayAllowed(false);
-    }
+    @Config(shadows = {ShadowAppInfoBase.class})
+    public void hideNonSystemOverlaysWhenResumed() {
+        when(mFragment.getActivity()).thenReturn(mActivity);
+        when(mActivity.getWindow()).thenReturn(mWindow);
 
-    @Test
-    @Config(shadows = ShadowPreferenceFragment.class)
-    public void onStop_enableOverlay() {
-        mFragment.onStop();
-        verify(mTouchOverlayManager).setOverlayAllowed(true);
+        mFragment.onResume();
+        mFragment.onPause();
+
+        InOrder inOrder = Mockito.inOrder(mWindow);
+        inOrder.verify(mWindow).addFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        inOrder.verify(mWindow).clearFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        inOrder.verifyNoMoreInteractions();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
index 3b3b482..d705610 100644
--- a/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
@@ -39,6 +39,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+/**
+ * deprecated in favor of {@link BugReportPreferenceControllerV2}
+ */
+@Deprecated
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class BugReportPreferenceControllerTest {
diff --git a/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerV2Test.java
new file mode 100644
index 0000000..9b4cde6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerV2Test.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BugReportPreferenceControllerV2Test {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+
+    private BugReportPreferenceControllerV2 mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        mController = new BugReportPreferenceControllerV2(mContext);
+    }
+
+    @Test
+    public void isAvailable_hasDebugRestriction_shouldBeFalse() {
+        when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_noDebugRestriction_shouldBeTrue() {
+        when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
new file mode 100644
index 0000000..1810b11
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.android.settings.development.FileEncryptionPreferenceController
+        .FILE_ENCRYPTION_PROPERTY_KEY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.storage.IStorageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {SettingsShadowSystemProperties.class})
+public class FileEncryptionPreferenceControllerTest {
+
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private IStorageManager mStorageManager;
+
+    private Context mContext;
+    private FileEncryptionPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new FileEncryptionPreferenceController(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+    }
+
+    @After
+    public void teardown() {
+        SettingsShadowSystemProperties.clear();
+    }
+
+    @Test
+    public void isAvailable_storageManagerNull_shouldBeFalse() {
+        ReflectionHelpers.setField(mController, "mStorageManager", null);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_notConvertibleToFBE_shouldBeFalse() throws RemoteException {
+        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
+        when(mStorageManager.isConvertibleToFBE()).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_convertibleToFBE_shouldBeTrue() throws RemoteException {
+        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
+        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void updateState_settingIsNotFile_shouldDoNothing() throws RemoteException {
+        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
+        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
+        mController.displayPreference(mPreferenceScreen);
+        SystemProperties.set(FILE_ENCRYPTION_PROPERTY_KEY, "foobar");
+
+        mController.updateState(mPreference);
+
+        verify(mPreference, never()).setEnabled(anyBoolean());
+        verify(mPreference, never()).setSummary(anyString());
+    }
+
+    @Test
+    public void updateState_settingIsFile_shouldSetSummaryAndDisablePreference()
+            throws RemoteException {
+        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
+        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
+        mController.displayPreference(mPreferenceScreen);
+        SystemProperties.set(FILE_ENCRYPTION_PROPERTY_KEY, "file");
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setSummary(
+                mContext.getResources().getString(R.string.convert_to_file_encryption_done));
+    }
+}
+
+
diff --git a/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java
new file mode 100644
index 0000000..0aab0db
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java
@@ -0,0 +1,166 @@
+package com.android.settings.development;
+
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+        .REQUEST_MOCK_LOCATION_APP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Collections;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class MockLocationAppPreferenceControllerTest {
+
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
+    private PackageManagerWrapper mPackageManager;
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private AppOpsManager.PackageOps mPackageOps;
+    @Mock
+    private AppOpsManager.OpEntry mOpEntry;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+
+    private Context mContext;
+    private MockLocationAppPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new MockLocationAppPreferenceController(mContext, mFragment));
+        ReflectionHelpers.setField(mController, "mAppsOpsManager", mAppOpsManager);
+        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void updateState_foobarAppSelected_shouldSetSummaryToFoobar() {
+        final String appName = "foobar";
+        when(mAppOpsManager.getPackagesForOps(any())).thenReturn(
+                Collections.singletonList(mPackageOps));
+        when(mPackageOps.getOps()).thenReturn(Collections.singletonList(mOpEntry));
+        when(mOpEntry.getMode()).thenReturn(AppOpsManager.MODE_ALLOWED);
+        when(mPackageOps.getPackageName()).thenReturn(appName);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(
+                mContext.getResources().getString(R.string.mock_location_app_set, appName));
+    }
+
+    @Test
+    public void updateState_noAppSelected_shouldSetSummaryToDefault() {
+        when(mAppOpsManager.getPackagesForOps(any())).thenReturn(
+                Collections.emptyList());
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(
+                mContext.getResources().getString(R.string.mock_location_app_not_set));
+    }
+
+    @Test
+    public void onActivityResult_fooPrevAppBarNewApp_shouldRemoveFooAndSetBarAsMockLocationApp()
+            throws PackageManager.NameNotFoundException {
+        final String prevAppName = "foo";
+        final String newAppName = "bar";
+        final Intent intent = new Intent();
+        intent.setAction(newAppName);
+        when(mAppOpsManager.getPackagesForOps(any())).thenReturn(
+                Collections.singletonList(mPackageOps));
+        when(mPackageOps.getOps()).thenReturn(Collections.singletonList(mOpEntry));
+        when(mOpEntry.getMode()).thenReturn(AppOpsManager.MODE_ALLOWED);
+        when(mPackageOps.getPackageName()).thenReturn(prevAppName);
+        when(mPackageManager.getApplicationInfo(anyString(),
+                eq(PackageManager.MATCH_DISABLED_COMPONENTS))).thenReturn(mApplicationInfo);
+
+        final boolean handled = mController.onActivityResult(REQUEST_MOCK_LOCATION_APP,
+                Activity.RESULT_OK, intent);
+
+        assertThat(handled).isTrue();
+        verify(mAppOpsManager).setMode(anyInt(), anyInt(), eq(newAppName),
+                eq(AppOpsManager.MODE_ALLOWED));
+        verify(mAppOpsManager).setMode(anyInt(), anyInt(), eq(prevAppName),
+                eq(AppOpsManager.MODE_ERRORED));
+    }
+
+    @Test
+    public void onActivityResult_incorrectCode_shouldReturnFalse() {
+        final boolean handled = mController.onActivityResult(30983150 /* request code */,
+                Activity.RESULT_OK, null /* intent */);
+
+        assertThat(handled).isFalse();
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_samePreferenceKey_shouldLaunchActivity() {
+        final String preferenceKey = mController.getPreferenceKey();
+        when(mPreference.getKey()).thenReturn(preferenceKey);
+
+        final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(handled).isTrue();
+        verify(mFragment).startActivityForResult(any(), eq(REQUEST_MOCK_LOCATION_APP));
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_incorrectPreferenceKey_shouldReturnFalse() {
+        when(mPreference.getKey()).thenReturn("SomeRandomKey");
+
+        assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse();
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() {
+        mController.onDeveloperOptionsSwitchEnabled();
+
+        verify(mPreference).setEnabled(true);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceControllerTest.java
new file mode 100644
index 0000000..0a189cb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/ShortcutManagerThrottlingPreferenceControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.IShortcutService;
+import android.os.RemoteException;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ShortcutManagerThrottlingPreferenceControllerTest {
+
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private IShortcutService mShortcutService;
+
+    private Context mContext;
+    private ShortcutManagerThrottlingPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new ShortcutManagerThrottlingPreferenceController(mContext);
+        ReflectionHelpers.setField(mController, "mShortcutService", mShortcutService);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_differentPreferenceKey_shouldReturnFalse() {
+        when(mPreference.getKey()).thenReturn("SomeRandomKey");
+
+        assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse();
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_correctPreferenceKey_shouldResetThrottling()
+            throws RemoteException {
+        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+
+        final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(handled).isTrue();
+        verify(mShortcutService).resetThrottling();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java
new file mode 100644
index 0000000..3d95cf4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.featureflags;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class FeatureFlagPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    private Context mContext;
+    private Lifecycle mLifecycle;
+    private FeatureFlagsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mLifecycle = new Lifecycle();
+        mController = new FeatureFlagsPreferenceController(mContext, mLifecycle);
+        when(mScreen.getContext()).thenReturn(mContext);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void verifyConstants() {
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mController.getPreferenceKey()).isNull();
+    }
+
+    @Test
+    public void onStart_shouldRefreshFeatureFlags() {
+        mLifecycle.onStart();
+
+        verify(mScreen).removeAll();
+        verify(mScreen).addPreference(any(FeatureFlagPreference.class));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java
new file mode 100644
index 0000000..11099b1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.featureflags;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class FeatureFlagPreferenceTest {
+
+    private static final String KEY = "feature_key";
+
+    private Context mContext;
+    private FeatureFlagPreference mPreference;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mPreference = new FeatureFlagPreference(mContext, KEY);
+    }
+
+    @Test
+    public void constructor_shouldSetTitleAndSummary() {
+        assertThat(mPreference.getTitle()).isEqualTo(KEY);
+        assertThat(mPreference.getSummary()).isEqualTo("false");
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void toggle_shouldUpdateSummary() {
+        mPreference.setChecked(true);
+
+        assertThat(mPreference.getSummary()).isEqualTo("true");
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/SerialNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/SerialNumberPreferenceControllerTest.java
deleted file mode 100644
index 18ef003..0000000
--- a/tests/robotests/src/com/android/settings/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.deviceinfo;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SerialNumberPreferenceControllerTest {
-
-    @Mock(answer = RETURNS_DEEP_STUBS)
-    private Context mContext;
-    @Mock(answer = RETURNS_DEEP_STUBS)
-    private PreferenceScreen mScreen;
-
-    private SerialNumberPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testIsAvaiable_noSerial_shouldReturnFalse() {
-        mController = new SerialNumberPreferenceController(mContext, null);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void testIsAvaiable_hasSerial_shouldReturnTrue() {
-        mController = new SerialNumberPreferenceController(mContext, "123");
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void testDisplay_noSerial_shouldHidePreference() {
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        mController = new SerialNumberPreferenceController(mContext, null);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
-
-        mController.displayPreference(mScreen);
-
-        verify(mScreen).removePreference(any(Preference.class));
-    }
-
-    @Test
-    public void testDisplay_hasSerial_shouldSummary() {
-        final String serial = "123";
-        final Preference preference = mock(Preference.class);
-        when(mScreen.findPreference(anyString())).thenReturn(preference);
-
-        mController = new SerialNumberPreferenceController(mContext, serial);
-        mController.displayPreference(mScreen);
-
-        verify(mScreen, never()).removePreference(any(Preference.class));
-        verify(preference).setSummary(serial);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAppInfoBase.java
similarity index 81%
rename from tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java
rename to tests/robotests/src/com/android/settings/testutils/shadow/ShadowAppInfoBase.java
index cfd0ce9..12173c8 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPreferenceFragment.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAppInfoBase.java
@@ -16,20 +16,20 @@
 
 package com.android.settings.testutils.shadow;
 
-import android.support.v14.preference.PreferenceFragment;
+import com.android.settings.applications.AppInfoBase;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
-@Implements(PreferenceFragment.class)
-public class ShadowPreferenceFragment {
+@Implements(AppInfoBase.class)
+public class ShadowAppInfoBase {
 
     @Implementation
-    public void onStart() {
+    public void onResume() {
         // No-op.
     }
 
     @Implementation
-    public void onStop() {
+    public void onPause() {
         // No-op.
     }
 }