Merge "Switch to recyclerview in ManageApplications"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 386f0c2..f07c39f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1896,11 +1896,13 @@
                 android:icon="@drawable/ic_settings_development"
                 android:taskAffinity="com.android.settings"
                 android:parentActivityName="Settings">
+            <!-- Enable if we need to revert to the old development settings
             <intent-filter android:priority="1">
                 <action android:name="android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
                 <action android:name="com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            -->
             <intent-filter android:priority="50">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
@@ -1919,14 +1921,11 @@
                   android:icon="@drawable/ic_settings_development"
                   android:taskAffinity="com.android.settings"
                   android:parentActivityName="Settings">
-
-            <!-- Enable when deleting DevelopmentSettingsActivity
             <intent-filter android:priority="1">
                 <action android:name="android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
                 <action android:name="com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            -->
             <intent-filter android:priority="50">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
@@ -1994,9 +1993,9 @@
         <activity-alias android:name="DevelopmentSettings"
                   android:exported="true"
                   android:label="@string/development_settings_title"
-                  android:targetActivity="Settings$DevelopmentSettingsActivity">
+                  android:targetActivity="Settings$DevelopmentSettingsDashboardActivity">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                android:value="com.android.settings.development.DevelopmentSettings" />
+                android:value="com.android.settings.development.DevelopmentSettingsDashboardFragment" />
         </activity-alias>
 
         <activity android:name=".development.AppPicker"
@@ -2011,7 +2010,7 @@
         <activity-alias android:name="UsbSettings"
                   android:exported="true"
                   android:label="@string/storage_title_usb"
-                  android:targetActivity="Settings$DevelopmentSettingsActivity">
+                  android:targetActivity="Settings$DevelopmentSettingsDashboardActivity">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.deviceinfo.UsbSettings" />
         </activity-alias>
diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml
index 196ed9f..d2a1644 100644
--- a/res/xml/app_default_settings.xml
+++ b/res/xml/app_default_settings.xml
@@ -74,8 +74,8 @@
         android:title="@string/domain_urls_title"
         android:fragment="com.android.settings.applications.ManageDomainUrls" />
 
-    <com.android.settings.WorkOnlyCategory
-        android:key="work_defaults"
+    <com.android.settings.widget.WorkOnlyCategory
+        android:key="work_app_defaults"
         android:title="@string/default_for_work">
 
         <Preference
@@ -93,6 +93,6 @@
             <extra android:name="for_work" android:value="true" />
         </Preference>
 
-    </com.android.settings.WorkOnlyCategory>
+    </com.android.settings.widget.WorkOnlyCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index a764d7a..4d19099 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -156,7 +156,7 @@
               android:targetClass="com.android.cellbroadcastreceiver.CellBroadcastSettings" />
     </com.android.settingslib.RestrictedPreference>
 
-    <com.android.settings.WorkOnlyCategory
+    <com.android.settings.widget.WorkOnlyCategory
         android:key="sound_work_settings_section"
         android:title="@string/sound_work_settings"
         android:order="100">
@@ -193,5 +193,5 @@
                     android:ringtoneType="alarm"
                     android:dependency="work_use_personal_sounds" />
 
-    </com.android.settings.WorkOnlyCategory>
+    </com.android.settings.widget.WorkOnlyCategory>
 </PreferenceScreen>
diff --git a/res/xml/sound_settings_new_advance_button.xml b/res/xml/sound_settings_new_advance_button.xml
index 6b0ba0e..a1e1a32 100644
--- a/res/xml/sound_settings_new_advance_button.xml
+++ b/res/xml/sound_settings_new_advance_button.xml
@@ -154,7 +154,7 @@
               android:targetClass="com.android.cellbroadcastreceiver.CellBroadcastSettings" />
     </com.android.settingslib.RestrictedPreference>
 
-    <com.android.settings.WorkOnlyCategory
+    <com.android.settings.widget.WorkOnlyCategory
         android:key="sound_work_settings_section"
         android:title="@string/sound_work_settings"
         android:order="100">
@@ -191,5 +191,5 @@
                     android:ringtoneType="alarm"
                     android:dependency="work_use_personal_sounds" />
 
-    </com.android.settings.WorkOnlyCategory>
+    </com.android.settings.widget.WorkOnlyCategory>
 </PreferenceScreen>
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 2377ca5..eac3801 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -73,12 +73,12 @@
      * @deprecated in favor of {@link DevelopmentSettingsDashboardActivity}.
      */
     @Deprecated
-    public static class DevelopmentSettingsActivity extends SettingsActivity { /* empty */ }
-    public static class DevelopmentSettingsDashboardActivity extends SettingsActivity {
+    public static class DevelopmentSettingsActivity extends SettingsActivity {
         public static final boolean isEnabled() {
-            return FeatureFlagUtils.isEnabled("dev_option_v2");
+            return FeatureFlagUtils.isEnabled("dev_option_v1");
         }
     }
+    public static class DevelopmentSettingsDashboardActivity extends SettingsActivity { /* empty */ }
     public static class AccessibilitySettingsActivity extends SettingsActivity { /* empty */ }
     public static class CaptioningSettingsActivity extends SettingsActivity { /* empty */ }
     public static class AccessibilityInversionSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 973c216..1202d13 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -832,16 +832,16 @@
 
         final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
                 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
-        final boolean useDevOptionV2 = Settings.DevelopmentSettingsDashboardActivity.isEnabled();
+        final boolean useDevOptionV1 = Settings.DevelopmentSettingsActivity.isEnabled();
         // Enable old Dev option if v2 is disabled
         somethingChanged = setTileEnabled(new ComponentName(packageName,
                         Settings.DevelopmentSettingsActivity.class.getName()),
-                showDev && !useDevOptionV2, isAdmin)
+                showDev && useDevOptionV1, isAdmin)
                 || somethingChanged;
         // Enable new Dev option if v2 is enable
         somethingChanged = setTileEnabled(new ComponentName(packageName,
                         Settings.DevelopmentSettingsDashboardActivity.class.getName()),
-                showDev && useDevOptionV2, isAdmin)
+                showDev && !useDevOptionV1, isAdmin)
                 || somethingChanged;
 
         // Enable/disable backup settings depending on whether the user is admin.
diff --git a/src/com/android/settings/applications/DefaultAppSettings.java b/src/com/android/settings/applications/DefaultAppSettings.java
index bcbc917..1487da3 100644
--- a/src/com/android/settings/applications/DefaultAppSettings.java
+++ b/src/com/android/settings/applications/DefaultAppSettings.java
@@ -26,11 +26,12 @@
 import com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultEmergencyPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultHomePreferenceController;
+import com.android.settings.applications.defaultapps.DefaultPaymentSettingsPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultPhonePreferenceController;
 import com.android.settings.applications.defaultapps.DefaultSmsPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultWorkBrowserPreferenceController;
+import com.android.settings.widget.WorkOnlyCategoryPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultWorkPhonePreferenceController;
-import com.android.settings.applications.defaultapps.DefaultPaymentSettingsPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -45,6 +46,7 @@
 
     static final String TAG = "DefaultAppSettings";
 
+    private static final String KEY_DEFAULT_WORK_CATEGORY = "work_app_defaults";
     private static final String KEY_ASSIST_VOICE_INPUT = "assist_and_voice_input";
 
     @Override
@@ -69,12 +71,16 @@
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        final List<AbstractPreferenceController> workControllers = new ArrayList<>();
+        workControllers.add(new DefaultWorkPhonePreferenceController(context));
+        workControllers.add(new DefaultWorkBrowserPreferenceController(context));
+        controllers.addAll(workControllers);
+        controllers.add(new WorkOnlyCategoryPreferenceController(
+                context, KEY_DEFAULT_WORK_CATEGORY, workControllers));
         controllers.add(new DefaultAssistPreferenceController(context, KEY_ASSIST_VOICE_INPUT,
                 false /* showSetting */));
         controllers.add(new DefaultBrowserPreferenceController(context));
-        controllers.add(new DefaultWorkBrowserPreferenceController(context));
         controllers.add(new DefaultPhonePreferenceController(context));
-        controllers.add(new DefaultWorkPhonePreferenceController(context));
         controllers.add(new DefaultSmsPreferenceController(context));
         controllers.add(new DefaultEmergencyPreferenceController(context));
         controllers.add(new DefaultHomePreferenceController(context));
@@ -97,10 +103,8 @@
                     List<String> keys = super.getNonIndexableKeys(context);
                     keys.add(KEY_ASSIST_VOICE_INPUT);
                     // TODO (b/38230148) Remove these keys when we can differentiate work results
-                    keys.add((new DefaultWorkPhonePreferenceController(context))
-                            .getPreferenceKey());
-                    keys.add((new DefaultWorkBrowserPreferenceController(context))
-                            .getPreferenceKey());
+                    keys.add(DefaultWorkPhonePreferenceController.KEY);
+                    keys.add(DefaultWorkBrowserPreferenceController.KEY);
                     return keys;
                 }
 
diff --git a/src/com/android/settings/applications/defaultapps/DefaultWorkBrowserPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultWorkBrowserPreferenceController.java
index 0792ff9..4dc984b 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultWorkBrowserPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultWorkBrowserPreferenceController.java
@@ -23,6 +23,7 @@
 
 public class DefaultWorkBrowserPreferenceController extends DefaultBrowserPreferenceController {
 
+    public static final String KEY = "work_default_browser";
     private final UserHandle mUserHandle;
 
     public DefaultWorkBrowserPreferenceController(Context context) {
@@ -35,7 +36,7 @@
 
     @Override
     public String getPreferenceKey() {
-        return "work_default_browser";
+        return KEY;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/defaultapps/DefaultWorkPhonePreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultWorkPhonePreferenceController.java
index c6d04fa..decff06 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultWorkPhonePreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultWorkPhonePreferenceController.java
@@ -23,6 +23,7 @@
 
 public class DefaultWorkPhonePreferenceController extends DefaultPhonePreferenceController {
 
+    public static final String KEY = "work_default_phone_app";
     private final UserHandle mUserHandle;
 
     public DefaultWorkPhonePreferenceController(Context context) {
@@ -43,6 +44,6 @@
 
     @Override
     public String getPreferenceKey() {
-        return "work_default_phone_app";
+        return KEY;
     }
 }
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 0485e69..9d47c65 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -53,7 +53,7 @@
 
     private static final String KEY_BT_SCAN = "bt_scan";
 
-    // Copied from DevelopmentSettings.java
+    // Copied from BluetoothDeviceNoNamePreferenceController.java
     private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
             "persist.bluetooth.showdeviceswithoutnames";
 
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index 17e556e..0744862 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -85,11 +85,11 @@
                 SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);
         PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndInputSettings.class.getName(),
                 CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
-        if (Settings.DevelopmentSettingsDashboardActivity.isEnabled()) {
-            PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(),
+        if (Settings.DevelopmentSettingsActivity.isEnabled()) {
+            PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettings.class.getName(),
                     CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
         } else {
-            PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettings.class.getName(),
+            PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(),
                     CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
         }
         PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(),
diff --git a/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceController.java b/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceController.java
index bb03df1..e4e6493 100644
--- a/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceController.java
+++ b/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceController.java
@@ -40,17 +40,15 @@
     static final int STREAMING_LABEL_ID = R.string.bluetooth_select_a2dp_codec_streaming_label;
 
     protected final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
-    protected final Object mBluetoothA2dpLock;
     protected BluetoothA2dp mBluetoothA2dp;
+    protected ListPreference mPreference;
     private final String[] mListValues;
     private final String[] mListSummaries;
-    private ListPreference mPreference;
 
     public AbstractBluetoothA2dpPreferenceController(Context context, Lifecycle lifecycle,
-            Object bluetoothA2dpLock, BluetoothA2dpConfigStore store) {
+            BluetoothA2dpConfigStore store) {
         super(context);
 
-        mBluetoothA2dpLock = bluetoothA2dpLock;
         mBluetoothA2dpConfigStore = store;
         mListValues = getListValues();
         mListSummaries = getListSummaries();
@@ -80,7 +78,7 @@
         writeConfigurationValues(newValue);
 
         final BluetoothCodecConfig codecConfig = mBluetoothA2dpConfigStore.createCodecConfig();
-        synchronized (mBluetoothA2dpLock) {
+        synchronized (mBluetoothA2dpConfigStore) {
             if (mBluetoothA2dp != null) {
                 setCodecConfigPreference(codecConfig);
             }
@@ -106,7 +104,7 @@
         }
 
         BluetoothCodecConfig codecConfig;
-        synchronized (mBluetoothA2dpLock) {
+        synchronized (mBluetoothA2dpConfigStore) {
             codecConfig = getCodecConfig();
         }
 
@@ -168,7 +166,7 @@
     protected abstract String[] getListSummaries();
 
     /**
-     * Updates the new value to the {@link BluetoothA2dpConfigStore}.
+     * Updates the new value to the {@link BluetoothA2dpConfigStore} and the {@link BluetoothA2dp}.
      *
      * @param newValue the new setting value
      */
@@ -197,18 +195,4 @@
 
         return mBluetoothA2dp.getCodecStatus().getCodecConfig();
     }
-
-    @VisibleForTesting
-    BluetoothCodecConfig createCodecConfig(int codecTypeValue, int codecPriorityValue,
-            int sampleRateValue, int bitsPerSampleValue,
-            int channelModeValue, long codecSpecific1Value,
-            long codecSpecific2Value, long codecSpecific3Value,
-            long codecSpecific4Value) {
-        return new BluetoothCodecConfig(codecTypeValue, codecPriorityValue,
-                sampleRateValue, bitsPerSampleValue,
-                channelModeValue, codecSpecific1Value,
-                codecSpecific2Value, codecSpecific3Value,
-                codecSpecific4Value);
-    }
-
 }
diff --git a/src/com/android/settings/development/BluetoothA2dpLock.java b/src/com/android/settings/development/BluetoothA2dpLock.java
new file mode 100644
index 0000000..7058665
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothA2dpLock.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bluetooth.BluetoothA2dp;
+
+/**
+ * Utility class to provide synchronization locks for {@link BluetoothA2dp}
+ */
+public class BluetoothA2dpLock {
+}
diff --git a/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceController.java b/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceController.java
new file mode 100644
index 0000000..f237ac1
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceController.java
@@ -0,0 +1,102 @@
+/*
+ * 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.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class BluetoothAudioBitsPerSamplePreferenceController extends
+        AbstractBluetoothA2dpPreferenceController {
+
+    private static final int DEFAULT_INDEX = 0;
+    private static final String BLUETOOTH_SELECT_A2DP_BITS_PER_SAMPLE_KEY =
+            "bluetooth_select_a2dp_bits_per_sample";
+
+    public BluetoothAudioBitsPerSamplePreferenceController(Context context, Lifecycle lifecycle,
+            BluetoothA2dpConfigStore store) {
+        super(context, lifecycle, store);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return BLUETOOTH_SELECT_A2DP_BITS_PER_SAMPLE_KEY;
+    }
+
+    @Override
+    protected String[] getListValues() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_bits_per_sample_values);
+    }
+
+    @Override
+    protected String[] getListSummaries() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_bits_per_sample_summaries);
+    }
+
+    @Override
+    protected int getDefaultIndex() {
+        return DEFAULT_INDEX;
+    }
+
+    @Override
+    protected void writeConfigurationValues(Object newValue) {
+        final int index = mPreference.findIndexOfValue(newValue.toString());
+        int bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; // default
+        switch (index) {
+            case 0:
+                // Reset to default
+                break;
+            case 1:
+                bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_16;
+                break;
+            case 2:
+                bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_24;
+                break;
+            case 3:
+                bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_32;
+                break;
+            default:
+                break;
+        }
+        mBluetoothA2dpConfigStore.setBitsPerSample(bitsPerSampleValue);
+    }
+
+    @Override
+    protected int getCurrentA2dpSettingIndex(BluetoothCodecConfig config) {
+        final int bitsPerSample = config.getBitsPerSample();
+        int index = DEFAULT_INDEX;
+        switch (bitsPerSample) {
+            case BluetoothCodecConfig.BITS_PER_SAMPLE_16:
+                index = 1;
+                break;
+            case BluetoothCodecConfig.BITS_PER_SAMPLE_24:
+                index = 2;
+                break;
+            case BluetoothCodecConfig.BITS_PER_SAMPLE_32:
+                index = 3;
+                break;
+            case BluetoothCodecConfig.BITS_PER_SAMPLE_NONE:
+            default:
+                break;
+        }
+        return index;
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothAudioChannelModePreferenceController.java b/src/com/android/settings/development/BluetoothAudioChannelModePreferenceController.java
new file mode 100644
index 0000000..a58c675
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothAudioChannelModePreferenceController.java
@@ -0,0 +1,96 @@
+/*
+ * 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.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class BluetoothAudioChannelModePreferenceController extends
+        AbstractBluetoothA2dpPreferenceController {
+
+    private static final int DEFAULT_INDEX = 0;
+    private static final String BLUETOOTH_SELECT_A2DP_CHANNEL_MODE_KEY =
+            "bluetooth_select_a2dp_channel_mode";
+
+    public BluetoothAudioChannelModePreferenceController(Context context, Lifecycle lifecycle,
+            BluetoothA2dpConfigStore store) {
+        super(context, lifecycle, store);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return BLUETOOTH_SELECT_A2DP_CHANNEL_MODE_KEY;
+    }
+
+    @Override
+    protected String[] getListValues() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_channel_mode_values);
+    }
+
+    @Override
+    protected String[] getListSummaries() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_channel_mode_summaries);
+    }
+
+    @Override
+    protected int getDefaultIndex() {
+        return DEFAULT_INDEX;
+    }
+
+    @Override
+    protected void writeConfigurationValues(Object newValue) {
+        final int index = mPreference.findIndexOfValue(newValue.toString());
+        int channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_NONE; // default
+        switch (index) {
+            case 0:
+                // Reset to default
+                break;
+            case 1:
+                channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+                break;
+            case 2:
+                channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                break;
+            default:
+                break;
+        }
+        mBluetoothA2dpConfigStore.setChannelMode(channelModeValue);
+    }
+
+    @Override
+    protected int getCurrentA2dpSettingIndex(BluetoothCodecConfig config) {
+        final int channelMode = config.getChannelMode();
+        int index = DEFAULT_INDEX;
+        switch (channelMode) {
+            case BluetoothCodecConfig.CHANNEL_MODE_MONO:
+                index = 1;
+                break;
+            case BluetoothCodecConfig.CHANNEL_MODE_STEREO:
+                index = 2;
+                break;
+            case BluetoothCodecConfig.CHANNEL_MODE_NONE:
+            default:
+                break;
+        }
+        return index;
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothAudioCodecPreferenceController.java b/src/com/android/settings/development/BluetoothAudioCodecPreferenceController.java
new file mode 100644
index 0000000..2163a70
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothAudioCodecPreferenceController.java
@@ -0,0 +1,156 @@
+/*
+ * 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.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class BluetoothAudioCodecPreferenceController extends
+        AbstractBluetoothA2dpPreferenceController {
+
+    private static final int DEFAULT_INDEX = 0;
+    private static final String BLUETOOTH_SELECT_A2DP_CODEC_KEY = "bluetooth_select_a2dp_codec";
+
+    public BluetoothAudioCodecPreferenceController(Context context, Lifecycle lifecycle,
+            BluetoothA2dpConfigStore store) {
+        super(context, lifecycle, store);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return BLUETOOTH_SELECT_A2DP_CODEC_KEY;
+    }
+
+    @Override
+    protected String[] getListValues() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_values);
+    }
+
+    @Override
+    protected String[] getListSummaries() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_summaries);
+    }
+
+    @Override
+    protected int getDefaultIndex() {
+        return DEFAULT_INDEX;
+    }
+
+    @Override
+    protected void writeConfigurationValues(Object newValue) {
+        final int index = mPreference.findIndexOfValue(newValue.toString());
+        int codecTypeValue = BluetoothCodecConfig.SAMPLE_RATE_NONE; // default
+        int codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+        switch (index) {
+            case 0:
+                // Reset the priority of the current codec to default
+                final String oldValue = mPreference.getValue();
+                switch (mPreference.findIndexOfValue(oldValue)) {
+                    case 0:
+                        break;      // No current codec
+                    case 1:
+                        codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
+                        break;
+                    case 2:
+                        codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC;
+                        break;
+                    case 3:
+                        codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
+                        break;
+                    case 4:
+                        codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
+                        break;
+                    case 5:
+                        codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case 1:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
+                codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+                break;
+            case 2:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC;
+                codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+                break;
+            case 3:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
+                codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+                break;
+            case 4:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
+                codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+                break;
+            case 5:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+                codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+                break;
+            case 6:
+                synchronized (mBluetoothA2dpConfigStore) {
+                    if (mBluetoothA2dp != null) {
+                        mBluetoothA2dp.enableOptionalCodecs();
+                    }
+                }
+                return;
+            case 7:
+                synchronized (mBluetoothA2dpConfigStore) {
+                    if (mBluetoothA2dp != null) {
+                        mBluetoothA2dp.disableOptionalCodecs();
+                    }
+                }
+                return;
+            default:
+                break;
+        }
+        mBluetoothA2dpConfigStore.setCodecType(codecTypeValue);
+        mBluetoothA2dpConfigStore.setCodecPriority(codecPriorityValue);
+    }
+
+    @Override
+    protected int getCurrentA2dpSettingIndex(BluetoothCodecConfig config) {
+        final int codecType = config.getCodecType();
+        int index = DEFAULT_INDEX;
+        switch (codecType) {
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
+                index = 1;
+                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
+                index = 2;
+                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
+                index = 3;
+                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
+                index = 4;
+                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
+                index = 5;
+                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID:
+            default:
+                break;
+        }
+        return index;
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothAudioQualityPreferenceController.java b/src/com/android/settings/development/BluetoothAudioQualityPreferenceController.java
new file mode 100644
index 0000000..7adf8e6
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothAudioQualityPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * 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.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class BluetoothAudioQualityPreferenceController extends
+        AbstractBluetoothA2dpPreferenceController {
+
+    private static final int DEFAULT_INDEX = 3;
+    private static final String BLUETOOTH_SELECT_A2DP_LDAC_PLAYBACK_QUALITY_KEY =
+            "bluetooth_select_a2dp_ldac_playback_quality";
+
+    public BluetoothAudioQualityPreferenceController(Context context, Lifecycle lifecycle,
+            BluetoothA2dpConfigStore store) {
+        super(context, lifecycle, store);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return BLUETOOTH_SELECT_A2DP_LDAC_PLAYBACK_QUALITY_KEY;
+    }
+
+    @Override
+    protected String[] getListValues() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_ldac_playback_quality_values);
+    }
+
+    @Override
+    protected String[] getListSummaries() {
+        return mContext.getResources().getStringArray(
+                R.array.bluetooth_a2dp_codec_ldac_playback_quality_summaries);
+    }
+
+    @Override
+    protected int getDefaultIndex() {
+        return DEFAULT_INDEX;
+    }
+
+    @Override
+    protected void writeConfigurationValues(Object newValue) {
+        final int index = mPreference.findIndexOfValue(newValue.toString());
+        int codecSpecific1Value = 0; // default
+        switch (index) {
+            case 0:
+            case 1:
+            case 2:
+            case 3:
+                codecSpecific1Value = 1000 + index;
+                break;
+            default:
+                break;
+        }
+        mBluetoothA2dpConfigStore.setCodecSpecific1Value(codecSpecific1Value);
+    }
+
+    @Override
+    protected int getCurrentA2dpSettingIndex(BluetoothCodecConfig config) {
+        // The actual values are 0, 1, 2 - those are extracted
+        // as mod-10 remainders of a larger value.
+        // The reason is because within BluetoothCodecConfig we cannot use
+        // a codec-specific value of zero.
+        int index = (int) config.getCodecSpecific1();
+        if (index > 0) {
+            index %= 10;
+        } else {
+            index = DEFAULT_INDEX;
+        }
+        switch (index) {
+            case 0:
+            case 1:
+            case 2:
+            case 3:
+                break;
+            default:
+                index = DEFAULT_INDEX;
+                break;
+        }
+        return index;
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceController.java b/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceController.java
index 5d84de8..35b449e 100644
--- a/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceController.java
+++ b/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceController.java
@@ -18,8 +18,6 @@
 
 import android.bluetooth.BluetoothCodecConfig;
 import android.content.Context;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -31,11 +29,9 @@
     private static final String BLUETOOTH_SELECT_A2DP_SAMPLE_RATE_KEY =
             "bluetooth_select_a2dp_sample_rate";
 
-    private ListPreference mPreference;
-
     public BluetoothAudioSampleRatePreferenceController(Context context, Lifecycle lifecycle,
-            Object bluetoothA2dpLock, BluetoothA2dpConfigStore store) {
-        super(context, lifecycle, bluetoothA2dpLock, store);
+            BluetoothA2dpConfigStore store) {
+        super(context, lifecycle, store);
     }
 
     @Override
@@ -44,13 +40,6 @@
     }
 
     @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-
-        mPreference = (ListPreference) screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
     protected String[] getListValues() {
         return mContext.getResources().getStringArray(
                 R.array.bluetooth_a2dp_codec_sample_rate_values);
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index e425fd2..c80416b 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -60,7 +60,8 @@
 
     private static final String TAG = "DevSettingsDashboard";
 
-    private final Object mBluetoothA2dpLock = new Object();
+    private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore =
+            new BluetoothA2dpConfigStore();
 
     private boolean mIsAvailable = true;
     private SwitchBar mSwitchBar;
@@ -104,7 +105,7 @@
                 @Override
                 public void onServiceConnected(int profile,
                         BluetoothProfile proxy) {
-                    synchronized (mBluetoothA2dpLock) {
+                    synchronized (mBluetoothA2dpConfigStore) {
                         mBluetoothA2dp = (BluetoothA2dp) proxy;
                     }
                     for (AbstractPreferenceController controller : mPreferenceControllers) {
@@ -117,7 +118,7 @@
 
                 @Override
                 public void onServiceDisconnected(int profile) {
-                    synchronized (mBluetoothA2dpLock) {
+                    synchronized (mBluetoothA2dpConfigStore) {
                         mBluetoothA2dp = null;
                     }
                     for (AbstractPreferenceController controller : mPreferenceControllers) {
@@ -295,7 +296,7 @@
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
         mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(),
-                this /* devOptionsDashboardFragment */, mBluetoothA2dpLock,
+                this /* devOptionsDashboardFragment */,
                 new BluetoothA2dpConfigStore());
         return mPreferenceControllers;
     }
@@ -349,7 +350,7 @@
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
             Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
-            Object bluetoothA2dpLock, BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
+            BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new BugReportPreferenceControllerV2(context));
         controllers.add(new LocalBackupPasswordPreferenceController(context));
@@ -391,13 +392,16 @@
         controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
         controllers.add(new BluetoothInbandRingingPreferenceController(context));
         controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
-        //controllers.add(new BluetoothAudioCodecPreferenceController(context, lifecycle,
-        //        bluetoothA2dpLock, bluetoothA2dpConfigStore));
+        controllers.add(new BluetoothAudioCodecPreferenceController(context, lifecycle,
+                bluetoothA2dpConfigStore));
         controllers.add(new BluetoothAudioSampleRatePreferenceController(context, lifecycle,
-                bluetoothA2dpLock, bluetoothA2dpConfigStore));
-        // bluetooth audio bits per sample
-        // bluetooth audio channel mode
-        // bluetooth audio ldac codec: playback quality
+                bluetoothA2dpConfigStore));
+        controllers.add(new BluetoothAudioBitsPerSamplePreferenceController(context, lifecycle,
+                bluetoothA2dpConfigStore));
+        controllers.add(new BluetoothAudioChannelModePreferenceController(context, lifecycle,
+                bluetoothA2dpConfigStore));
+        controllers.add(new BluetoothAudioQualityPreferenceController(context, lifecycle,
+                bluetoothA2dpConfigStore));
         controllers.add(new ShowTapsPreferenceController(context));
         controllers.add(new PointerLocationPreferenceController(context));
         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
@@ -463,7 +467,7 @@
                         context) {
                     return buildPreferenceControllers(context, null /* activity */,
                             null /* lifecycle */, null /* devOptionsDashboardFragment */,
-                            null /* bluetoothA2dpLock */, null /* bluetoothA2dpConfigStore */);
+                            null /* bluetoothA2dpConfigStore */);
                 }
             };
 }
diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
index ac318ff..2641f5d 100644
--- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
@@ -35,7 +35,6 @@
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
-import com.android.settings.development.DevelopmentSettings;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.RestrictedLockUtils;
@@ -221,9 +220,5 @@
         mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on,
                 Toast.LENGTH_LONG);
         mDevHitToast.show();
-        // This is good time to index the Developer Options
-        FeatureFactory.getFactory(mContext).getSearchFeatureProvider().getIndexingManager(mContext)
-                .updateFromClassNameResource(DevelopmentSettings.class.getName(),
-                        true /* includeInSearchResults */);
     }
 }
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 9608aea..1774615 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -21,6 +21,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.XmlRes;
 import android.text.TextUtils;
+
 import com.android.settings.DateTimeSettings;
 import com.android.settings.DeviceInfoSettings;
 import com.android.settings.DisplaySettings;
@@ -45,7 +46,7 @@
 import com.android.settings.datausage.DataUsageMeteredSettings;
 import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
-import com.android.settings.development.DevelopmentSettings;
+import com.android.settings.development.DevelopmentSettingsDashboardFragment;
 import com.android.settings.deviceinfo.Status;
 import com.android.settings.deviceinfo.StorageDashboardFragment;
 import com.android.settings.deviceinfo.StorageSettings;
@@ -90,6 +91,7 @@
 import com.android.settings.wifi.ConfigureWifiSettings;
 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
 import com.android.settings.wifi.WifiSettings;
+
 import java.util.Collection;
 import java.util.HashMap;
 
@@ -189,7 +191,8 @@
         addIndex(DateTimeSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_date_time);
         addIndex(AccessibilitySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_accessibility);
         addIndex(PrintSettingsFragment.class, NO_DATA_RES_ID, R.drawable.ic_settings_print);
-        addIndex(DevelopmentSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_development);
+        addIndex(DevelopmentSettingsDashboardFragment.class, NO_DATA_RES_ID,
+                R.drawable.ic_settings_development);
         addIndex(DeviceInfoSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about);
         addIndex(Status.class, NO_DATA_RES_ID, 0 /* icon */);
         addIndex(LegalSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about);
diff --git a/src/com/android/settings/WorkOnlyCategory.java b/src/com/android/settings/widget/WorkOnlyCategory.java
similarity index 90%
rename from src/com/android/settings/WorkOnlyCategory.java
rename to src/com/android/settings/widget/WorkOnlyCategory.java
index ca4c82d..53badb2 100644
--- a/src/com/android/settings/WorkOnlyCategory.java
+++ b/src/com/android/settings/widget/WorkOnlyCategory.java
@@ -12,13 +12,16 @@
  * permissions and limitations under the License.
  */
 
-package com.android.settings;
+package com.android.settings.widget;
 
 import android.content.Context;
 import android.os.UserManager;
 import android.support.v7.preference.PreferenceCategory;
 import android.util.AttributeSet;
 
+import com.android.settings.SelfAvailablePreference;
+import com.android.settings.Utils;
+
 /**
  * A PreferenceCategory that is only visible when the device has a work profile.
  */
diff --git a/src/com/android/settings/widget/WorkOnlyCategoryPreferenceController.java b/src/com/android/settings/widget/WorkOnlyCategoryPreferenceController.java
new file mode 100644
index 0000000..a308b2f
--- /dev/null
+++ b/src/com/android/settings/widget/WorkOnlyCategoryPreferenceController.java
@@ -0,0 +1,57 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.List;
+
+public class WorkOnlyCategoryPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    private final String mKey;
+    private final List<AbstractPreferenceController> mChildren;
+
+    public WorkOnlyCategoryPreferenceController(Context context,
+            String key, List<AbstractPreferenceController> childrenControllers) {
+        super(context);
+        mKey = key;
+        mChildren = childrenControllers;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (mChildren == null || mChildren.isEmpty()) {
+            return true;
+        }
+        // Category is available if any child is available
+        for (AbstractPreferenceController controller : mChildren) {
+            if (controller.isAvailable()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return mKey;
+    }
+}
diff --git a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
index 4ee97fa..85e99c3 100644
--- a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
+++ b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
@@ -1,3 +1,3 @@
-com.android.settings.development.DevelopmentSettingsDashboardFragment
+com.android.settings.development.DevelopmentSettings
 com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard
 com.android.settings.search.indexing.FakeSettingsFragment
diff --git a/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java b/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
index c6350ef..ee7bdb0 100644
--- a/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
+++ b/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
@@ -24,11 +24,31 @@
     public static final int SAMPLE_RATE_NONE = 0;
     public static final int SAMPLE_RATE_48000 = 0x1 << 1;
     public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
+    public static final int SOURCE_CODEC_TYPE_AAC = 1;
     public static final int CODEC_PRIORITY_DEFAULT = 0;
+    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
     public static final int BITS_PER_SAMPLE_NONE = 0;
     public static final int CHANNEL_MODE_NONE = 0;
+    public static final int BITS_PER_SAMPLE_24 =  0x1 << 1;
+    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
 
     public int getSampleRate() {
         return 0;
     }
+
+    public int getCodecType() {
+        return 0;
+    }
+
+    public int getBitsPerSample() {
+        return 0;
+    }
+
+    public int getChannelMode() {
+        return 0;
+    }
+
+    public long getCodecSpecific1() {
+        return 0;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceControllerTest.java
index e78dd7f..dd29627 100644
--- a/tests/robotests/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/AbstractBluetoothA2dpPreferenceControllerTest.java
@@ -74,7 +74,7 @@
         mContext = RuntimeEnvironment.application;
         mLifecycle = new Lifecycle();
         mController = spy(new AbstractBluetoothA2dpPreferenceControllerImpl(mContext, mLifecycle,
-                new Object(), mBluetoothA2dpConfigStore));
+                mBluetoothA2dpConfigStore));
         doReturn(mBluetoothCodecConfig).when(mController).getCodecConfig();
         doNothing().when(mController).setCodecConfigPreference(any());
         when(mBluetoothA2dpConfigStore.createCodecConfig()).thenReturn(mBluetoothCodecConfig);
@@ -138,8 +138,8 @@
             AbstractBluetoothA2dpPreferenceController {
 
         public AbstractBluetoothA2dpPreferenceControllerImpl(Context context,
-                Lifecycle lifecycle, Object bluetoothA2dpLock, BluetoothA2dpConfigStore store) {
-            super(context, lifecycle, bluetoothA2dpLock, store);
+                Lifecycle lifecycle, BluetoothA2dpConfigStore store) {
+            super(context, lifecycle, store);
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceControllerTest.java
new file mode 100644
index 0000000..2438b21
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothAudioBitsPerSamplePreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+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 BluetoothAudioBitsPerSamplePreferenceControllerTest {
+
+    @Mock
+    private BluetoothCodecConfig mBluetoothCodecConfig;
+    @Mock
+    private ListPreference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
+
+    /**
+     * 0: Use System Selection (Default)
+     * 1: 16 bits/sample
+     * 2: 24 bits/sample
+     * 3: 32 bits/sample
+     */
+    private String[] mListValues;
+    private Context mContext;
+    private BluetoothAudioBitsPerSamplePreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new BluetoothAudioBitsPerSamplePreferenceController(mContext,
+                new Lifecycle(), mBluetoothA2dpConfigStore));
+        mListValues = mController.getListValues();
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void writeConfigurationValues_option2_shouldWriteOption2ToSharedStore() {
+        when(mPreference.findIndexOfValue(mListValues[2])).thenReturn(2);
+        mController.writeConfigurationValues(mListValues[2]);
+
+        verify(mBluetoothA2dpConfigStore).setBitsPerSample(BluetoothCodecConfig.BITS_PER_SAMPLE_24);
+    }
+
+    @Test
+    public void writeConfigurationValues_default_shouldSetDefault() {
+        when(mPreference.findIndexOfValue(mListValues[0])).thenReturn(0);
+        mController.writeConfigurationValues(mListValues[0]);
+
+        verify(mBluetoothA2dpConfigStore).setBitsPerSample(
+                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_option2_shouldReturnSecondIndex() {
+        when(mBluetoothCodecConfig.getBitsPerSample()).thenReturn(
+                BluetoothCodecConfig.BITS_PER_SAMPLE_24);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(2);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_unknownOption_shouldReturnDefault() {
+        when(mBluetoothCodecConfig.getCodecType()).thenReturn(1381391835);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothAudioChannelModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothAudioChannelModePreferenceControllerTest.java
new file mode 100644
index 0000000..efcd240
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothAudioChannelModePreferenceControllerTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+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 BluetoothAudioChannelModePreferenceControllerTest {
+
+    @Mock
+    private BluetoothCodecConfig mBluetoothCodecConfig;
+    @Mock
+    private ListPreference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
+
+    /**
+     * 0: Use System Selection (Default)
+     * 1: Mono
+     * 2: Stereo
+     */
+    private String[] mListValues;
+    private Context mContext;
+    private BluetoothAudioChannelModePreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new BluetoothAudioChannelModePreferenceController(mContext,
+                new Lifecycle(), mBluetoothA2dpConfigStore));
+        mListValues = mController.getListValues();
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void writeConfigurationValues_option2_shouldWriteOption2ToSharedStore() {
+        when(mPreference.findIndexOfValue(mListValues[2])).thenReturn(2);
+        mController.writeConfigurationValues(mListValues[2]);
+
+        verify(mBluetoothA2dpConfigStore).setChannelMode(BluetoothCodecConfig.CHANNEL_MODE_STEREO);
+    }
+
+    @Test
+    public void writeConfigurationValues_default_shouldSetDefault() {
+        when(mPreference.findIndexOfValue(mListValues[0])).thenReturn(0);
+        mController.writeConfigurationValues(mListValues[0]);
+
+        verify(mBluetoothA2dpConfigStore).setChannelMode(
+                BluetoothCodecConfig.CHANNEL_MODE_NONE);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_option2_shouldReturnSecondIndex() {
+        when(mBluetoothCodecConfig.getChannelMode()).thenReturn(
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(2);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_unknownOption_shouldReturnDefault() {
+        when(mBluetoothCodecConfig.getCodecType()).thenReturn(1381391835);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothAudioCodecPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothAudioCodecPreferenceControllerTest.java
new file mode 100644
index 0000000..560a9c3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothAudioCodecPreferenceControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+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 BluetoothAudioCodecPreferenceControllerTest {
+
+    @Mock
+    private BluetoothCodecConfig mBluetoothCodecConfig;
+    @Mock
+    private ListPreference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
+
+    /**
+     * 0: Use System Selection (Default)
+     * 1: SBC
+     * 2: AAC
+     * 3: Qualcomm aptX audio
+     * 4: Qualcomm aptX HD audio
+     * 5: LDAC
+     * 6: Enable Optional Codecs
+     * 7: Disable Optional Codecs
+     */
+    private String[] mListValues;
+    private Context mContext;
+    private BluetoothAudioCodecPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new BluetoothAudioCodecPreferenceController(mContext, new Lifecycle(),
+                mBluetoothA2dpConfigStore));
+        mListValues = mController.getListValues();
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void writeConfigurationValues_option2_shouldWriteOption2ToSharedStore() {
+        when(mPreference.findIndexOfValue(mListValues[2])).thenReturn(2);
+        mController.writeConfigurationValues(mListValues[2]);
+
+        verify(mBluetoothA2dpConfigStore).setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC);
+        verify(mBluetoothA2dpConfigStore).setCodecPriority(
+                BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST);
+    }
+
+    @Test
+    public void writeConfigurationValues_default_shouldSetDefaultPriority() {
+        when(mPreference.findIndexOfValue(mListValues[0])).thenReturn(0);
+        mController.writeConfigurationValues(mListValues[0]);
+
+        verify(mBluetoothA2dpConfigStore).setCodecPriority(
+                BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_option2_shouldReturnSecondIndex() {
+        when(mBluetoothCodecConfig.getCodecType()).thenReturn(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(2);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_unknownOption_shouldReturnDefault() {
+        when(mBluetoothCodecConfig.getCodecType()).thenReturn(1381391835);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothAudioQualityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothAudioQualityPreferenceControllerTest.java
new file mode 100644
index 0000000..e00ac00
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothAudioQualityPreferenceControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+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 BluetoothAudioQualityPreferenceControllerTest {
+
+    @Mock
+    private BluetoothCodecConfig mBluetoothCodecConfig;
+    @Mock
+    private ListPreference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
+
+    /**
+     * 0: Optimized for Audio Quality (990kbps/909kbps)
+     * 1: Balanced Audio And Connection Quality (660kbps/606kbps)
+     * 2: Stereo
+     */
+    private String[] mListValues;
+    private Context mContext;
+    private BluetoothAudioQualityPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new BluetoothAudioQualityPreferenceController(mContext,
+                new Lifecycle(), mBluetoothA2dpConfigStore));
+        mListValues = mController.getListValues();
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void writeConfigurationValues_option3_shouldWrite1003ToSharedStore() {
+        when(mPreference.findIndexOfValue(mListValues[3])).thenReturn(3);
+        mController.writeConfigurationValues(mListValues[3]);
+
+        verify(mBluetoothA2dpConfigStore).setCodecSpecific1Value(1003);
+    }
+
+    @Test
+    public void writeConfigurationValues_default_shouldSetDefault() {
+        when(mPreference.findIndexOfValue(mListValues[0])).thenReturn(0);
+        mController.writeConfigurationValues(mListValues[0]);
+
+        verify(mBluetoothA2dpConfigStore).setCodecSpecific1Value(1000);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_option2_shouldReturnSecondIndex() {
+        when(mBluetoothCodecConfig.getCodecSpecific1()).thenReturn(Long.valueOf(2));
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(2);
+    }
+
+    @Test
+    public void getCurrentA2dpSettingIndex_unknownOption_shouldReturnDefault() {
+        when(mBluetoothCodecConfig.getCodecType()).thenReturn(1381391835);
+
+        final int index = mController.getCurrentA2dpSettingIndex(mBluetoothCodecConfig);
+
+        assertThat(index).isEqualTo(3);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceControllerTest.java
index 8919576..f683d21 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothAudioSampleRatePreferenceControllerTest.java
@@ -70,7 +70,7 @@
         mContext = RuntimeEnvironment.application;
         mLifecycle = new Lifecycle();
         mController = spy(new BluetoothAudioSampleRatePreferenceController(mContext, mLifecycle,
-                new Object(), mBluetoothA2dpConfigStore));
+                mBluetoothA2dpConfigStore));
         mListValues = mController.getListValues();
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
         mController.displayPreference(mScreen);
diff --git a/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java
index e061787..59be160 100644
--- a/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java
@@ -17,6 +17,14 @@
 package com.android.settings.widget;
 
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.text.InputType;
 import android.text.TextWatcher;
 import android.view.View;
@@ -33,14 +41,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-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 ValidatedEditTextPreferenceTest {
diff --git a/tests/robotests/src/com/android/settings/widget/WorkOnlyCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/widget/WorkOnlyCategoryPreferenceControllerTest.java
new file mode 100644
index 0000000..9c7e8fc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/WorkOnlyCategoryPreferenceControllerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WorkOnlyCategoryPreferenceControllerTest {
+
+    private Context mContext;
+    private WorkOnlyCategoryPreferenceController mController;
+    private List<AbstractPreferenceController> mChildren;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mChildren = new ArrayList<>();
+        mController = new WorkOnlyCategoryPreferenceController(mContext, "pref_key", mChildren);
+    }
+
+    @Test
+    public void isAvailable_noChildren_true() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_childrenAvailable_true() {
+        final AbstractPreferenceController child = mock(AbstractPreferenceController.class);
+        when(child.isAvailable()).thenReturn(true);
+        mChildren.add(child);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_childrenUnavailable_false() {
+        final AbstractPreferenceController child = mock(AbstractPreferenceController.class);
+        when(child.isAvailable()).thenReturn(false);
+        mChildren.add(child);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+}