Merge "Add new USB details screen for Connected Devices 2.0"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1766ffb..86cfbfc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2012,13 +2012,21 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".deviceinfo.UsbModeChooserActivity"
+        <activity android:name=".connecteddevice.usb.UsbModeChooserActivity"
                   android:excludeFromRecents="true"
                   android:exported="true"
                   android:permission="android.permission.MANAGE_USB"
                   android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar">
         </activity>
 
+        <activity android:name=".Settings$UsbDetailsActivity"
+                  android:excludeFromRecents="true"
+                  android:permission="android.permission.MANAGE_USB"
+                  android:exported="true">
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.connecteddevice.usb.UsbDetailsFragment"/>
+        </activity>
+
         <activity android:name=".RemoteBugreportActivity"
                   android:excludeFromRecents="true"
                   android:exported="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4cf03dd..9a6ff90 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8005,15 +8005,15 @@
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for powering the other device only. -->
-    <string name="usb_use_power_only">Supply power</string>
+    <string name="usb_use_power_only">Charging connected device</string>
     <!-- Decription of one of the choices in a dialog (with title defined in usb_use) that lets the
          user select what the USB connection for this device should be used for. This choice
          is for powering the other device. -->
-    <string name="usb_use_power_only_desc">Charge the connected device. Works only with devices that support USB charging.</string>
+    <string name="usb_use_power_only_desc">Other settings unavailable when turned on</string>
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring files via MTP. -->
-    <string name="usb_use_file_transfers">Transfer files</string>
+    <string name="usb_use_file_transfers">File Transfer</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring files via MTP. -->
@@ -8021,23 +8021,31 @@
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring photos via PTP. -->
-    <string name="usb_use_photo_transfers">Transfer photos (PTP)</string>
+    <string name="usb_use_photo_transfers">PTP</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring photos via PTP. -->
     <string name="usb_use_photo_transfers_desc">Transfer photos or files if MTP is not supported (PTP)</string>
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
+         is for USB tethering. -->
+    <string name="usb_use_tethering">USB tethering</string>
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
+         select what the USB connection for this device should be used for. This choice
          is for entering MIDI mode. -->
-    <string name="usb_use_MIDI">Use device as MIDI</string>
+    <string name="usb_use_MIDI">MIDI</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for entering MIDI mode. -->
     <string name="usb_use_MIDI_desc">Use this device as MIDI</string>
     <!-- The title used in a dialog which lets the user select what the USB connection
-         for this device should be used for. Choices are usb_use_charging_only,
-         usb_use_file_transfer, use_use_photo_transfer, and usb_use_MIDI -->
-    <string name="usb_use">Use USB to</string>
+         for this device should be used for. These options are more commonly used.
+         Choices are usb_use_file_transfer.-->
+    <string name="usb_use">Use USB for</string>
+    <!-- The title used in a dialog which lets the user select what the USB connection
+         for this device should be used for. These options are less commonly used.
+         Choices are usb_use_tethering, usb_use_photo_transfers, usb_use_MIDI, and usb_use_power_only.-->
+    <string name="usb_use_also">Also use USB for</string>
 
     <!-- Settings item title for USB preference [CHAR LIMIT=35] -->
     <string name="usb_pref">USB</string>
@@ -8045,13 +8053,27 @@
     <!-- Settings item summary for USB preference when set to charging only [CHAR LIMIT=NONE] -->
     <string name="usb_summary_charging_only">Charging this device</string>
     <!-- Settings item summary for USB preference when set to powering the other device only [CHAR LIMIT=NONE] -->
-    <string name="usb_summary_power_only">Supplying power</string>
+    <string name="usb_summary_power_only">Charging connected device</string>
     <!-- Settings item summary for USB preference when set to transferring files via MTP [CHAR LIMIT=NONE] -->
-    <string name="usb_summary_file_transfers">Transferring files</string>
+    <string name="usb_summary_file_transfers">File transfer</string>
+    <!-- Settings item summary for USB preference when set to USB tethering [CHAR LIMIT=NONE] -->
+    <string name="usb_summary_tether">USB tethering</string>
     <!-- Settings item summary for USB preference when set to transferring photos via PTP [CHAR LIMIT=NONE] -->
-    <string name="usb_summary_photo_transfers">Transferring photos (PTP)</string>
+    <string name="usb_summary_photo_transfers">PTP</string>
     <!-- Settings item summary for USB preference when set to entering MIDI mode [CHAR LIMIT=NONE] -->
-    <string name="usb_summary_MIDI">Using device as MIDI</string>
+    <string name="usb_summary_MIDI">MIDI</string>
+    <!-- Settings item summary for USB preference when set to transferring files via MTP
+          and powering other device [CHAR LIMIT=NONE] -->
+    <string name="usb_summary_file_transfers_power">File transfer and supplying power</string>
+    <!-- Settings item summary for USB preference when set to USB tethering
+         and powering other device [CHAR LIMIT=NONE] -->
+    <string name="usb_summary_tether_power">USB tethering and supplying power</string>
+    <!-- Settings item summary for USB preference when set to transferring photos via PTP
+         and powering other device [CHAR LIMIT=NONE] -->
+    <string name="usb_summary_photo_transfers_power">PTP and supplying power</string>
+    <!-- Settings item summary for USB preference when set to entering MIDI mode
+         and powering other device [CHAR LIMIT=NONE] -->
+    <string name="usb_summary_MIDI_power">MIDI and supplying power</string>
 
     <!-- Settings item title for SMS Mirroring preference [CHAR LIMIT=35] -->
     <string name="sms_mirroring_pref">SMS Mirroring</string>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 8ca6b81..0b75abf 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -57,16 +57,6 @@
         android:order="-2"/>
 
     <Preference
-        android:key="usb_mode"
-        android:title="@string/usb_pref"
-        android:icon="@drawable/ic_usb"
-        android:order="-1">
-        <intent android:action="android.intent.action.MAIN"
-                android:targetPackage="com.android.settings"
-                android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/>
-    </Preference>
-
-    <Preference
         android:key="bt_received_files"
         android:icon="@drawable/ic_folder_vd_theme_24"
         android:title="@string/bluetooth_show_received_files" />
diff --git a/res/xml/connected_devices_old.xml b/res/xml/connected_devices_old.xml
index 3eb62ea..cc7b5b4 100644
--- a/res/xml/connected_devices_old.xml
+++ b/res/xml/connected_devices_old.xml
@@ -53,7 +53,7 @@
         android:order="-2">
         <intent android:action="android.intent.action.MAIN"
                 android:targetPackage="com.android.settings"
-                android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/>
+                android:targetClass="com.android.settings.connecteddevice.usb.UsbModeChooserActivity"/>
     </Preference>
 
     <PreferenceCategory
diff --git a/res/xml/usb_details_fragment.xml b/res/xml/usb_details_fragment.xml
new file mode 100644
index 0000000..30ca993
--- /dev/null
+++ b/res/xml/usb_details_fragment.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/device_details_title">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="usb_device_header"
+        android:layout="@layout/settings_entity_header"
+        android:selectable="false"/>
+
+    <PreferenceCategory
+        android:key="usb_main_options"
+        android:title="@string/usb_use"/>
+
+    <PreferenceCategory
+        android:key="usb_secondary_options"
+        android:title="@string/usb_use_also"/>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 7bd85cd..741bfda 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -96,6 +96,7 @@
     public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
     public static class UsbSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class UsbDetailsActivity extends SettingsActivity { /* empty */ }
     public static class TrustedCredentialsSettingsActivity extends SettingsActivity { /* empty */ }
     public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ }
     public static class PrintSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
index 2a136bc..9ac6ebd 100644
--- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
@@ -24,8 +24,9 @@
 import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
 import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
 import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
+import com.android.settings.connecteddevice.usb.UsbBackend;
+import com.android.settings.connecteddevice.usb.UsbModePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.deviceinfo.UsbBackend;
 import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentOld.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentOld.java
index 7097b36..bde5e81 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentOld.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentOld.java
@@ -26,9 +26,10 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
 import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.usb.UsbBackend;
+import com.android.settings.connecteddevice.usb.UsbModePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.SummaryLoader;
-import com.android.settings.deviceinfo.UsbBackend;
 import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index 3cccc15..3d5d0e5 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -20,6 +20,7 @@
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceScreen;
 
+import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
@@ -48,7 +49,7 @@
     public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
         super(fragment.getContext());
         init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this),
-                new ConnectedUsbDeviceUpdater(fragment.getContext(), this));
+                new ConnectedUsbDeviceUpdater(fragment, this));
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java
deleted file mode 100644
index 07a7691..0000000
--- a/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java
+++ /dev/null
@@ -1,76 +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.connecteddevice;
-
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbManager;
-
-/**
- * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
- */
-public class UsbConnectionBroadcastReceiver extends BroadcastReceiver {
-    private Context mContext;
-    private UsbConnectionListener mUsbConnectionListener;
-    private boolean mListeningToUsbEvents;
-    private boolean mConnected;
-
-    public UsbConnectionBroadcastReceiver(Context context,
-            UsbConnectionListener usbConnectionListener) {
-        mContext = context;
-        mUsbConnectionListener = usbConnectionListener;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        mConnected = intent != null
-                && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-        if (mUsbConnectionListener != null) {
-            mUsbConnectionListener.onUsbConnectionChanged(mConnected);
-        }
-    }
-
-    public void register() {
-        if (!mListeningToUsbEvents) {
-            final IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-            final Intent intent = mContext.registerReceiver(this, intentFilter);
-            mConnected = intent != null
-                    && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-            mListeningToUsbEvents = true;
-        }
-    }
-
-    public void unregister() {
-        if (mListeningToUsbEvents) {
-            mContext.unregisterReceiver(this);
-            mListeningToUsbEvents = false;
-        }
-    }
-
-    public boolean isConnected() {
-        return mConnected;
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when usb connection is changed.
-     */
-    interface UsbConnectionListener {
-        void onUsbConnectionChanged(boolean connected);
-    }
-}
diff --git a/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java b/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdater.java
similarity index 65%
rename from src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java
rename to src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdater.java
index 0468b0f..dd29902 100644
--- a/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java
+++ b/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdater.java
@@ -13,22 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
 
 import android.content.Context;
-import android.content.Intent;
+import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
 
 import com.android.settings.R;
-import com.android.settings.deviceinfo.UsbBackend;
-import com.android.settings.deviceinfo.UsbModeChooserActivity;
+import com.android.settings.SettingsActivity;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.widget.GearPreference;
 
 /**
  * Controller to maintain connected usb device
  */
 public class ConnectedUsbDeviceUpdater {
-    private Context mContext;
+    private PreferenceFragment mFragment;
     private UsbBackend mUsbBackend;
     private DevicePreferenceCallback mDevicePreferenceCallback;
     @VisibleForTesting
@@ -36,8 +38,9 @@
     @VisibleForTesting
     UsbConnectionBroadcastReceiver mUsbReceiver;
 
-    private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
-            (connected) -> {
+    @VisibleForTesting
+    UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+            (connected, newMode) -> {
                 if (connected) {
                     mUsbPreference.setSummary(
                             UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode()));
@@ -47,18 +50,19 @@
                 }
             };
 
-    public ConnectedUsbDeviceUpdater(Context context,
+    public ConnectedUsbDeviceUpdater(DashboardFragment fragment,
             DevicePreferenceCallback devicePreferenceCallback) {
-        this(context, devicePreferenceCallback, new UsbBackend(context));
+        this(fragment, devicePreferenceCallback, new UsbBackend(fragment.getContext()));
     }
 
     @VisibleForTesting
-    ConnectedUsbDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback,
-            UsbBackend usbBackend) {
-        mContext = context;
+    ConnectedUsbDeviceUpdater(DashboardFragment fragment,
+            DevicePreferenceCallback devicePreferenceCallback, UsbBackend usbBackend) {
+        mFragment = fragment;
         mDevicePreferenceCallback = devicePreferenceCallback;
         mUsbBackend = usbBackend;
-        mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener);
+        mUsbReceiver = new UsbConnectionBroadcastReceiver(fragment.getContext(),
+                mUsbConnectionListener, mUsbBackend);
     }
 
     public void registerCallback() {
@@ -76,8 +80,12 @@
         mUsbPreference.setIcon(R.drawable.ic_usb);
         mUsbPreference.setSelectable(false);
         mUsbPreference.setOnGearClickListener((GearPreference p) -> {
-            final Intent intent = new Intent(mContext, UsbModeChooserActivity.class);
-            mContext.startActivity(intent);
+            // New version - uses a separate screen.
+            final Bundle args = new Bundle();
+            final SettingsActivity activity = (SettingsActivity) mFragment.getContext();
+            activity.startPreferencePanel(mFragment,
+                    UsbDetailsFragment.class.getName(), args,
+                    R.string.device_details_title, null /* titleText */, null /* resultTo */, 0);
         });
 
         forceUpdate();
@@ -87,6 +95,5 @@
         // Register so we can get the connection state from sticky intent.
         //TODO(b/70336520): Use an API to get data instead of sticky intent
         mUsbReceiver.register();
-        mUsbConnectionListener.onUsbConnectionChanged(mUsbReceiver.isConnected());
     }
 }
diff --git a/src/com/android/settings/connecteddevice/usb/OWNERS b/src/com/android/settings/connecteddevice/usb/OWNERS
new file mode 100644
index 0000000..add985c
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/OWNERS
@@ -0,0 +1,3 @@
+# Default reviewers for this and subdirectories.
+zhangjerry@google.com
+badhri@google.com
diff --git a/src/com/android/settings/connecteddevice/usb/UsbBackend.java b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
new file mode 100644
index 0000000..cdfb6b0
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015 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.connecteddevice.usb;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.net.ConnectivityManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+
+public class UsbBackend {
+
+    public static final int MODE_POWER_MASK  = 0x01;
+    public static final int MODE_POWER_SINK   = 0x00;
+    public static final int MODE_POWER_SOURCE = 0x01;
+
+    public static final int MODE_DATA_MASK  = 0x0f << 1;
+    public static final int MODE_DATA_NONE   = 0;
+    public static final int MODE_DATA_MTP    = 0x01 << 1;
+    public static final int MODE_DATA_PTP    = 0x01 << 2;
+    public static final int MODE_DATA_MIDI   = 0x01 << 3;
+    public static final int MODE_DATA_TETHER   = 0x01 << 4;
+
+    private final boolean mFileTransferRestricted;
+    private final boolean mFileTransferRestrictedBySystem;
+    private final boolean mTetheringRestricted;
+    private final boolean mTetheringRestrictedBySystem;
+    private final boolean mMidiSupported;
+    private final boolean mTetheringSupported;
+
+    private UsbManager mUsbManager;
+    @VisibleForTesting
+    UsbManagerPassThrough mUsbManagerPassThrough;
+    private UsbPort mPort;
+    private UsbPortStatus mPortStatus;
+
+    private Context mContext;
+
+    public UsbBackend(Context context) {
+        this(context, new UserRestrictionUtil(context), null);
+    }
+
+    @VisibleForTesting
+    public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil,
+            UsbManagerPassThrough usbManagerPassThrough) {
+        mContext = context;
+        mUsbManager = context.getSystemService(UsbManager.class);
+
+        mUsbManagerPassThrough = usbManagerPassThrough;
+        if (mUsbManagerPassThrough == null) {
+            mUsbManagerPassThrough = new UsbManagerPassThrough(mUsbManager);
+        }
+
+        mFileTransferRestricted = userRestrictionUtil.isUsbFileTransferRestricted();
+        mFileTransferRestrictedBySystem = userRestrictionUtil.isUsbFileTransferRestrictedBySystem();
+        mTetheringRestricted = userRestrictionUtil.isUsbTetheringRestricted();
+        mTetheringRestrictedBySystem = userRestrictionUtil.isUsbTetheringRestrictedBySystem();
+
+        mMidiSupported = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+        ConnectivityManager cm =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mTetheringSupported = cm.isTetheringSupported();
+
+        UsbPort[] ports = mUsbManager.getPorts();
+        if (ports == null) {
+            return;
+        }
+        // For now look for a connected port, in the future we should identify port in the
+        // notification and pick based on that.
+        final int N = ports.length;
+        for (int i = 0; i < N; i++) {
+            UsbPortStatus status = mUsbManager.getPortStatus(ports[i]);
+            if (status.isConnected()) {
+                mPort = ports[i];
+                mPortStatus = status;
+                break;
+            }
+        }
+    }
+
+    public int getCurrentMode() {
+        if (mPort != null) {
+            int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
+                    && mPortStatus.isConnected()
+                    ? MODE_POWER_SOURCE : MODE_POWER_SINK;
+            return power | getUsbDataMode();
+        }
+        return MODE_POWER_SINK | getUsbDataMode();
+    }
+
+    public int getUsbDataMode() {
+        long functions = mUsbManagerPassThrough.getCurrentFunctions();
+        if (functions == UsbManager.FUNCTION_MTP) {
+            return MODE_DATA_MTP;
+        } else if (functions == UsbManager.FUNCTION_PTP) {
+            return MODE_DATA_PTP;
+        } else if (functions == UsbManager.FUNCTION_MIDI) {
+            return MODE_DATA_MIDI;
+        } else if (functions == UsbManager.FUNCTION_RNDIS) {
+            return MODE_DATA_TETHER;
+        }
+        return MODE_DATA_NONE;
+    }
+
+    private void setUsbFunction(int mode) {
+        switch (mode) {
+            case MODE_DATA_MTP:
+                mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MTP);
+                break;
+            case MODE_DATA_PTP:
+                mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_PTP);
+                break;
+            case MODE_DATA_MIDI:
+                mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MIDI);
+                break;
+            case MODE_DATA_TETHER:
+                mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+                break;
+            default:
+                mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_NONE);
+                break;
+        }
+    }
+
+    public void setMode(int mode) {
+        if (mPort != null) {
+            int powerRole = modeToPower(mode);
+            // If we aren't using any data modes and we support host mode, then go to host mode
+            // so maybe? the other device can provide data if it wants, otherwise go into device
+            // mode because we have no choice.
+            int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE
+                    && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST)
+                    ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE;
+            mUsbManager.setPortRoles(mPort, powerRole, dataRole);
+        }
+        setUsbFunction(mode & MODE_DATA_MASK);
+    }
+
+    private int modeToPower(int mode) {
+        return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE
+                    ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK;
+    }
+
+    public boolean isModeDisallowed(int mode) {
+        if (mFileTransferRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
+                || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
+            return true;
+        } else if (mTetheringRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isModeDisallowedBySystem(int mode) {
+        if (mFileTransferRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
+                || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
+            return true;
+        } else if (mTetheringRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isModeSupported(int mode) {
+        if (!mMidiSupported && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
+            return false;
+        }
+        if (!mTetheringSupported && (mode & MODE_DATA_MASK) == MODE_DATA_TETHER) {
+                return false;
+        }
+        if (mPort != null) {
+            int power = modeToPower(mode);
+            if ((mode & MODE_DATA_MASK) != 0) {
+                // We have a port and data, need to be in device mode.
+                return mPortStatus.isRoleCombinationSupported(power,
+                        UsbPort.DATA_ROLE_DEVICE);
+            } else {
+                // No data needed, we can do this power mode in either device or host.
+                return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE)
+                        || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST);
+            }
+        }
+        // No port, support sink modes only.
+        return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE;
+    }
+
+    // Wrapper class to enable testing with UserManager APIs
+    public static class UserRestrictionUtil {
+        private UserManager mUserManager;
+
+        public UserRestrictionUtil(Context context) {
+            mUserManager = UserManager.get(context);
+        }
+
+        public boolean isUsbFileTransferRestricted() {
+            return mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
+        }
+
+        public boolean isUsbTetheringRestricted() {
+            return mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+        }
+
+        public boolean isUsbFileTransferRestrictedBySystem() {
+            return mUserManager.hasBaseUserRestriction(
+                UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId()));
+        }
+
+        public boolean isUsbTetheringRestrictedBySystem() {
+            return mUserManager.hasBaseUserRestriction(
+                UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(UserHandle.myUserId()));
+        }
+    }
+
+    // Temporary pass-through to allow roboelectric to use getCurrentFunctions()
+    public static class UsbManagerPassThrough {
+        private UsbManager mUsbManager;
+
+        public UsbManagerPassThrough(UsbManager manager) {
+            mUsbManager = manager;
+        }
+
+        public long getCurrentFunctions() {
+            return mUsbManager.getCurrentFunctions();
+        }
+
+        public long usbFunctionsFromString(String str) {
+            return UsbManager.usbFunctionsFromString(str);
+        }
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java
new file mode 100644
index 0000000..91d22dc
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java
@@ -0,0 +1,129 @@
+/*
+ * 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.connecteddevice.usb;
+
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+
+/**
+ * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
+ */
+public class UsbConnectionBroadcastReceiver extends BroadcastReceiver implements LifecycleObserver,
+        OnResume, OnPause {
+    private Context mContext;
+    private UsbConnectionListener mUsbConnectionListener;
+    private boolean mListeningToUsbEvents;
+    private int mMode;
+    private boolean mConnected;
+    private UsbBackend mUsbBackend;
+
+    public UsbConnectionBroadcastReceiver(Context context,
+            UsbConnectionListener usbConnectionListener, UsbBackend backend) {
+        mContext = context;
+        mUsbConnectionListener = usbConnectionListener;
+        mUsbBackend = backend;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+            mConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED)
+                    || intent.getExtras().getBoolean(UsbManager.USB_HOST_CONNECTED);
+            if (mConnected) {
+                mMode &= UsbBackend.MODE_POWER_MASK;
+                if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MTP)
+                        && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
+                    mMode |= UsbBackend.MODE_DATA_MTP;
+                }
+                if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_PTP)
+                        && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
+                    mMode |= UsbBackend.MODE_DATA_PTP;
+                }
+                if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MIDI)) {
+                    mMode |= UsbBackend.MODE_DATA_MIDI;
+                }
+                if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_RNDIS)) {
+                    mMode |= UsbBackend.MODE_DATA_TETHER;
+                }
+            }
+        } else if (UsbManager.ACTION_USB_PORT_CHANGED.equals(intent.getAction())) {
+            mMode &= UsbBackend.MODE_DATA_MASK;
+            UsbPortStatus portStatus = intent.getExtras()
+                    .getParcelable(UsbManager.EXTRA_PORT_STATUS);
+            if (portStatus != null) {
+                mConnected = portStatus.isConnected();
+                if (mConnected) {
+                    mMode |= portStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
+                            ? UsbBackend.MODE_POWER_SOURCE : UsbBackend.MODE_POWER_SINK;
+                }
+            }
+        }
+        if (mUsbConnectionListener != null) {
+            mUsbConnectionListener.onUsbConnectionChanged(mConnected, mMode);
+        }
+    }
+
+    public void register() {
+        if (!mListeningToUsbEvents) {
+            mMode = mUsbBackend.getCurrentMode();
+            mConnected = false;
+            final IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(UsbManager.ACTION_USB_STATE);
+            intentFilter.addAction(UsbManager.ACTION_USB_PORT_CHANGED);
+            mContext.registerReceiver(this, intentFilter);
+            mListeningToUsbEvents = true;
+        }
+    }
+
+    public void unregister() {
+        if (mListeningToUsbEvents) {
+            mContext.unregisterReceiver(this);
+            mListeningToUsbEvents = false;
+        }
+    }
+
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    @Override
+    public void onResume() {
+        register();
+    }
+
+    @Override
+    public void onPause() {
+        unregister();
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when usb connection is changed.
+     */
+    interface UsbConnectionListener {
+        void onUsbConnectionChanged(boolean connected, int newMode);
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java
new file mode 100644
index 0000000..09c7554
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import android.content.Context;
+import android.support.annotation.UiThread;
+import android.support.v14.preference.PreferenceFragment;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This class provides common members and refresh functionality for usb controllers.
+ */
+public abstract class UsbDetailsController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    protected final Context mContext;
+    protected final PreferenceFragment mFragment;
+    protected final UsbBackend mUsbBackend;
+
+    public UsbDetailsController(Context context, PreferenceFragment fragment, UsbBackend backend) {
+        super(context);
+        mContext = context;
+        mFragment = fragment;
+        mUsbBackend = backend;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    /**
+     * This method is called when the USB mode has changed and the controller needs to update.
+     * @param newMode the new mode, made up of OR'd values from UsbBackend
+     */
+    @UiThread
+    protected abstract void refresh(int newMode);
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
new file mode 100644
index 0000000..c861188
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls the USB device details and provides updates to individual controllers.
+ */
+public class UsbDetailsFragment extends DashboardFragment {
+    private static final String TAG = UsbDetailsFragment.class.getSimpleName();
+
+    private List<UsbDetailsController> mControllers;
+    private UsbBackend mUsbBackend;
+
+    @VisibleForTesting
+    UsbConnectionBroadcastReceiver mUsbReceiver;
+
+    private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+            (connected, newMode) -> {
+                if (!connected) {
+                    this.finish();
+                } else {
+                    for (UsbDetailsController controller : mControllers) {
+                        controller.refresh(newMode);
+                    }
+                }
+            };
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.USB_DEVICE_DETAILS;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.usb_details_fragment;
+    }
+
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        super.onCreatePreferences(savedInstanceState, rootKey);
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        mUsbBackend = new UsbBackend(context);
+        mControllers = createControllerList(context, mUsbBackend, this);
+        mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener,
+                mUsbBackend);
+        this.getLifecycle().addObserver(mUsbReceiver);
+
+        List<AbstractPreferenceController> ret = new ArrayList<>();
+        ret.addAll(mControllers);
+        return ret;
+    }
+
+    private static List<UsbDetailsController> createControllerList(Context context,
+            UsbBackend usbBackend, DashboardFragment fragment) {
+        List<UsbDetailsController> ret = new ArrayList<>();
+        ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));
+        ret.add(new UsbDetailsProfilesController(context, fragment,
+                usbBackend, Lists.newArrayList(UsbManager.USB_FUNCTION_MTP), "usb_main_options"));
+        ret.add(new UsbDetailsProfilesController(context, fragment,
+                usbBackend, Lists.newArrayList(UsbDetailsProfilesController.KEY_POWER,
+                UsbManager.USB_FUNCTION_RNDIS, UsbManager.USB_FUNCTION_MIDI,
+                UsbManager.USB_FUNCTION_PTP), "usb_secondary_options"));
+        return ret;
+    }
+
+    /**
+     * For Search.
+     */
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    return new ArrayList<>();
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    return super.getNonIndexableKeys(context);
+                }
+
+                @Override
+                public List<AbstractPreferenceController> getPreferenceControllers(
+                        Context context) {
+                    List<AbstractPreferenceController> ret = new ArrayList<>();
+                    ret.addAll(createControllerList(context, new UsbBackend(context), null));
+                    return ret;
+                }
+            };
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java
new file mode 100644
index 0000000..7ac0235
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.EntityHeaderController;
+
+/**
+ * This class adds a header with device name and current function.
+ */
+public class UsbDetailsHeaderController extends UsbDetailsController {
+    private static final String KEY_DEVICE_HEADER = "usb_device_header";
+
+    private EntityHeaderController mHeaderController;
+
+    public UsbDetailsHeaderController(Context context, PreferenceFragment fragment,
+            UsbBackend backend) {
+        super(context, fragment, backend);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        final LayoutPreference headerPreference =
+                (LayoutPreference) screen.findPreference(KEY_DEVICE_HEADER);
+        mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
+                headerPreference.findViewById(R.id.entity_header));
+        screen.addPreference(headerPreference);
+    }
+
+
+    @Override
+    protected void refresh(int newMode) {
+        mHeaderController.setLabel(mContext.getString(R.string.usb_pref));
+        mHeaderController.setIcon(mContext.getDrawable(R.drawable.ic_usb));
+        mHeaderController.setSummary(
+                mContext.getString(UsbModePreferenceController.getSummary(newMode)));
+        mHeaderController.done(mFragment.getActivity(), true /* rebindActions */);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_DEVICE_HEADER;
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java
new file mode 100644
index 0000000..1375b4c
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import com.android.settings.R;
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+
+import java.util.List;
+
+/**
+ * This class adds switches for toggling individual USB options, such as "transfer files",
+ * "supply power", "usb tethering", etc.
+ */
+public class UsbDetailsProfilesController extends UsbDetailsController
+        implements Preference.OnPreferenceClickListener {
+
+    static final String KEY_POWER = "power";
+
+    private PreferenceCategory mProfilesContainer;
+    private List<String> mOptions;
+    private String mKey;
+
+    public UsbDetailsProfilesController(Context context, PreferenceFragment fragment,
+            UsbBackend backend, List<String> options, String key) {
+        super(context, fragment, backend);
+        mOptions = options;
+        mKey = key;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mProfilesContainer = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+    }
+
+    /**
+     * Gets a switch preference for the particular option, creating it if needed.
+     */
+    private SwitchPreference getProfilePreference(String key, int titleId) {
+        SwitchPreference pref = (SwitchPreference) mProfilesContainer.findPreference(key);
+        if (pref == null) {
+            pref = new SwitchPreference(mProfilesContainer.getContext());
+            pref.setKey(key);
+            pref.setTitle(titleId);
+            pref.setOnPreferenceClickListener(this);
+            mProfilesContainer.addPreference(pref);
+        }
+        return pref;
+    }
+
+    @Override
+    protected void refresh(int mode) {
+        SwitchPreference pref;
+        for (String option : mOptions) {
+            int newMode;
+            int summary = -1;
+            int title;
+            if (option.equals(UsbManager.USB_FUNCTION_MTP)) {
+                newMode = UsbBackend.MODE_DATA_MTP;
+                title = R.string.usb_use_file_transfers;
+            } else if (option.equals(KEY_POWER)) {
+                newMode = UsbBackend.MODE_POWER_SOURCE;
+                title = R.string.usb_use_power_only;
+                summary = R.string.usb_use_power_only_desc;
+            } else if (option.equals(UsbManager.USB_FUNCTION_PTP)) {
+                newMode = UsbBackend.MODE_DATA_PTP;
+                title = R.string.usb_use_photo_transfers;
+            } else if (option.equals(UsbManager.USB_FUNCTION_MIDI)) {
+                newMode = UsbBackend.MODE_DATA_MIDI;
+                title = R.string.usb_use_MIDI;
+            } else if (option.equals(UsbManager.USB_FUNCTION_RNDIS)) {
+                newMode = UsbBackend.MODE_DATA_TETHER;
+                title = R.string.usb_use_tethering;
+            } else {
+                continue;
+            }
+
+            pref = getProfilePreference(option, title);
+            // Only show supported and allowed options
+            if (mUsbBackend.isModeSupported(newMode)
+                    && !mUsbBackend.isModeDisallowedBySystem(newMode)
+                    && !mUsbBackend.isModeDisallowed(newMode)) {
+                if (summary != -1) {
+                    pref.setSummary(summary);
+                }
+                pref.setChecked((mode & newMode) != 0);
+            } else {
+                mProfilesContainer.removePreference(pref);
+            }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        SwitchPreference profilePref = (SwitchPreference) preference;
+        String key = profilePref.getKey();
+        int mode = mUsbBackend.getCurrentMode();
+        int thisMode = 0;
+        if (key.equals(KEY_POWER)) {
+            thisMode = UsbBackend.MODE_POWER_SOURCE;
+        } else if (key.equals(UsbManager.USB_FUNCTION_MTP)) {
+            thisMode = UsbBackend.MODE_DATA_MTP;
+        } else if (key.equals(UsbManager.USB_FUNCTION_PTP)) {
+            thisMode = UsbBackend.MODE_DATA_PTP;
+        } else if (key.equals(UsbManager.USB_FUNCTION_RNDIS)) {
+            thisMode = UsbBackend.MODE_DATA_TETHER;
+        } else if (key.equals(UsbManager.USB_FUNCTION_MIDI)) {
+            thisMode = UsbBackend.MODE_DATA_MIDI;
+        }
+        if (profilePref.isChecked()) {
+            if (!key.equals(KEY_POWER)) {
+                // Only one non power mode can currently be set at once.
+                mode &= UsbBackend.MODE_POWER_MASK;
+            }
+            mode |= thisMode;
+        } else {
+            mode &= ~thisMode;
+        }
+        mUsbBackend.setMode(mode);
+        return false;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return mKey;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java b/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivity.java
similarity index 98%
rename from src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
rename to src/com/android/settings/connecteddevice/usb/UsbModeChooserActivity.java
index 8ba3781..b3b0718 100644
--- a/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
 
 import android.annotation.Nullable;
 import android.app.Activity;
diff --git a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java b/src/com/android/settings/connecteddevice/usb/UsbModePreferenceController.java
similarity index 72%
rename from src/com/android/settings/connecteddevice/UsbModePreferenceController.java
rename to src/com/android/settings/connecteddevice/usb/UsbModePreferenceController.java
index 8693520..e342460 100644
--- a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbModePreferenceController.java
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
 
 import android.content.Context;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.deviceinfo.UsbBackend;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -33,27 +33,27 @@
     private static final String KEY_USB_MODE = "usb_mode";
 
     private UsbBackend mUsbBackend;
-    private UsbConnectionBroadcastReceiver mUsbReceiver;
+    @VisibleForTesting
+    UsbConnectionBroadcastReceiver mUsbReceiver;
     private Preference mUsbPreference;
 
     public UsbModePreferenceController(Context context, UsbBackend usbBackend) {
         super(context);
         mUsbBackend = usbBackend;
-        mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected) -> {
-            updateSummary(mUsbPreference);
-        });
+        mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected, newMode) -> {
+            updateSummary(mUsbPreference, connected, newMode);
+        }, mUsbBackend);
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mUsbPreference = screen.findPreference(KEY_USB_MODE);
-        updateSummary(mUsbPreference);
     }
 
     @Override
     public void updateState(Preference preference) {
-        updateSummary(preference);
+        updateSummary(preference, mUsbReceiver.isConnected(), mUsbBackend.getCurrentMode());
     }
 
     @Override
@@ -88,17 +88,24 @@
                 return R.string.usb_summary_photo_transfers;
             case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI:
                 return R.string.usb_summary_MIDI;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_TETHER:
+                return R.string.usb_summary_tether;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_MTP:
+                return R.string.usb_summary_file_transfers_power;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_PTP:
+                return R.string.usb_summary_photo_transfers_power;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_MIDI:
+                return R.string.usb_summary_MIDI_power;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_TETHER:
+                return R.string.usb_summary_tether_power;
+            default:
+                return R.string.usb_summary_charging_only;
         }
-        return 0;
     }
 
-    private void updateSummary(Preference preference) {
-        updateSummary(preference, mUsbBackend.getCurrentMode());
-    }
-
-    private void updateSummary(Preference preference, int mode) {
+    private void updateSummary(Preference preference, boolean connected, int mode) {
         if (preference != null) {
-            if (mUsbReceiver.isConnected()) {
+            if (connected) {
                 preference.setEnabled(true);
                 preference.setSummary(getSummary(mode));
             } else {
@@ -107,5 +114,4 @@
             }
         }
     }
-
 }
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index af85ac9..f43c3c8 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -58,6 +58,7 @@
 import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
 import com.android.settings.datausage.DataPlanUsageSummary;
 import com.android.settings.datausage.DataUsageList;
 import com.android.settings.datausage.DataUsageSummary;
@@ -242,6 +243,7 @@
             NetworkDashboardFragment.class.getName(),
             ConnectedDeviceDashboardFragment.class.getName(),
             ConnectedDeviceDashboardFragmentOld.class.getName(),
+            UsbDetailsFragment.class.getName(),
             AppAndNotificationDashboardFragment.class.getName(),
             AccountDashboardFragment.class.getName(),
             EnterprisePrivacySettings.class.getName(),
diff --git a/src/com/android/settings/development/SelectUsbConfigPreferenceController.java b/src/com/android/settings/development/SelectUsbConfigPreferenceController.java
index 77a9a75..63eb24c 100644
--- a/src/com/android/settings/development/SelectUsbConfigPreferenceController.java
+++ b/src/com/android/settings/development/SelectUsbConfigPreferenceController.java
@@ -27,11 +27,11 @@
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.connecteddevice.usb.UsbBackend;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnCreate;
@@ -48,6 +48,8 @@
     private final String[] mListValues;
     private final String[] mListSummaries;
     private final UsbManager mUsbManager;
+    @VisibleForTesting
+    UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
     private BroadcastReceiver mUsbReceiver;
     private ListPreference mPreference;
 
@@ -57,6 +59,7 @@
         mListValues = context.getResources().getStringArray(R.array.usb_configuration_values);
         mListSummaries = context.getResources().getStringArray(R.array.usb_configuration_titles);
         mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+        mUsbManagerPassThrough = new UsbBackend.UsbManagerPassThrough(mUsbManager);
         mUsbReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -95,7 +98,8 @@
             return false;
         }
 
-        writeUsbConfigurationOption(newValue.toString());
+        writeUsbConfigurationOption(mUsbManagerPassThrough
+                .usbFunctionsFromString(newValue.toString()));
         updateUsbConfigurationValues();
         return true;
     }
@@ -129,14 +133,15 @@
     }
 
     @VisibleForTesting
-    void setCurrentFunction(String newValue, boolean usbDataUnlocked) {
-        mUsbManager.setCurrentFunction(newValue, usbDataUnlocked);
+    void setCurrentFunctions(long functions) {
+        mUsbManager.setCurrentFunctions(functions);
     }
 
     private void updateUsbConfigurationValues() {
+        long functions = mUsbManagerPassThrough.getCurrentFunctions();
         int index = 0;
         for (int i = 0; i < mListValues.length; i++) {
-            if (mUsbManager.isFunctionEnabled(mListValues[i])) {
+            if (functions == mUsbManagerPassThrough.usbFunctionsFromString(mListValues[i])) {
                 index = i;
                 break;
             }
@@ -145,11 +150,7 @@
         mPreference.setSummary(mListSummaries[index]);
     }
 
-    private void writeUsbConfigurationOption(String newValue) {
-        if (TextUtils.equals(newValue, "none")) {
-            setCurrentFunction(newValue, false);
-        } else {
-            setCurrentFunction(newValue, true);
-        }
+    private void writeUsbConfigurationOption(long newValue) {
+        setCurrentFunctions(newValue);
     }
 }
diff --git a/src/com/android/settings/deviceinfo/UsbBackend.java b/src/com/android/settings/deviceinfo/UsbBackend.java
deleted file mode 100644
index 5d2502b..0000000
--- a/src/com/android/settings/deviceinfo/UsbBackend.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2015 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.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.hardware.usb.UsbManager;
-import android.hardware.usb.UsbPort;
-import android.hardware.usb.UsbPortStatus;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.support.annotation.VisibleForTesting;
-
-public class UsbBackend {
-
-    public static final int MODE_POWER_MASK  = 0x01;
-    public static final int MODE_POWER_SINK   = 0x00;
-    public static final int MODE_POWER_SOURCE = 0x01;
-
-    public static final int MODE_DATA_MASK  = 0x03 << 1;
-    public static final int MODE_DATA_NONE   = 0x00 << 1;
-    public static final int MODE_DATA_MTP    = 0x01 << 1;
-    public static final int MODE_DATA_PTP    = 0x02 << 1;
-    public static final int MODE_DATA_MIDI   = 0x03 << 1;
-
-    private final boolean mRestricted;
-    private final boolean mRestrictedBySystem;
-    private final boolean mMidi;
-
-    private UsbManager mUsbManager;
-    private UsbPort mPort;
-    private UsbPortStatus mPortStatus;
-
-    private Context mContext;
-
-    public UsbBackend(Context context) {
-        this(context, new UserRestrictionUtil(context));
-    }
-
-    @VisibleForTesting
-    public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil) {
-        mContext = context;
-        mUsbManager = context.getSystemService(UsbManager.class);
-
-        mRestricted = userRestrictionUtil.isUsbFileTransferRestricted();
-        mRestrictedBySystem = userRestrictionUtil.isUsbFileTransferRestrictedBySystem();
-        mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
-
-        UsbPort[] ports = mUsbManager.getPorts();
-        if (ports == null) {
-            return;
-        }
-        // For now look for a connected port, in the future we should identify port in the
-        // notification and pick based on that.
-        final int N = ports.length;
-        for (int i = 0; i < N; i++) {
-            UsbPortStatus status = mUsbManager.getPortStatus(ports[i]);
-            if (status.isConnected()) {
-                mPort = ports[i];
-                mPortStatus = status;
-                break;
-            }
-        }
-    }
-
-    public int getCurrentMode() {
-        if (mPort != null) {
-            int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
-                    ? MODE_POWER_SOURCE : MODE_POWER_SINK;
-            return power | getUsbDataMode();
-        }
-        return MODE_POWER_SINK | getUsbDataMode();
-    }
-
-    public int getUsbDataMode() {
-        if (!isUsbDataUnlocked()) {
-            return MODE_DATA_NONE;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
-            return MODE_DATA_MTP;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) {
-            return MODE_DATA_PTP;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) {
-            return MODE_DATA_MIDI;
-        }
-        return MODE_DATA_NONE; // ...
-    }
-
-    private boolean isUsbDataUnlocked() {
-        Intent intent = mContext.registerReceiver(null,
-            new IntentFilter(UsbManager.ACTION_USB_STATE));
-        return intent == null ?
-            false : intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
-    }
-
-    private void setUsbFunction(int mode) {
-        switch (mode) {
-            case MODE_DATA_MTP:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true);
-                break;
-            case MODE_DATA_PTP:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true);
-                break;
-            case MODE_DATA_MIDI:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI, true);
-                break;
-            default:
-                mUsbManager.setCurrentFunction(null, false);
-                break;
-        }
-    }
-
-    public void setMode(int mode) {
-        if (mPort != null) {
-            int powerRole = modeToPower(mode);
-            // If we aren't using any data modes and we support host mode, then go to host mode
-            // so maybe? the other device can provide data if it wants, otherwise go into device
-            // mode because we have no choice.
-            int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE
-                    && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST)
-                    ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE;
-            mUsbManager.setPortRoles(mPort, powerRole, dataRole);
-        }
-        setUsbFunction(mode & MODE_DATA_MASK);
-    }
-
-    private int modeToPower(int mode) {
-        return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE
-                    ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK;
-    }
-
-    public boolean isModeDisallowed(int mode) {
-        if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
-                && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
-            // No USB data modes are supported.
-            return true;
-        }
-        return false;
-    }
-
-    public boolean isModeDisallowedBySystem(int mode) {
-        if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
-                && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
-            // No USB data modes are supported.
-            return true;
-        }
-        return false;
-    }
-
-    public boolean isModeSupported(int mode) {
-        if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
-            return false;
-        }
-
-        if (mPort != null) {
-            int power = modeToPower(mode);
-            if ((mode & MODE_DATA_MASK) != 0) {
-                // We have a port and data, need to be in device mode.
-                return mPortStatus.isRoleCombinationSupported(power,
-                        UsbPort.DATA_ROLE_DEVICE);
-            } else {
-                // No data needed, we can do this power mode in either device or host.
-                return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE)
-                        || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST);
-            }
-        }
-        // No port, support sink modes only.
-        return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE;
-    }
-
-    // Wrapper class to enable testing with UserManager APIs
-    public static class UserRestrictionUtil {
-        private UserManager mUserManager;
-
-        public UserRestrictionUtil(Context context) {
-            mUserManager = UserManager.get(context);
-        }
-
-        public boolean isUsbFileTransferRestricted() {
-            return mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
-        }
-
-        public boolean isUsbFileTransferRestrictedBySystem() {
-            return mUserManager.hasBaseUserRestriction(
-                UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId()));
-        }
-    }
-}
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
index 38dc15d..1edc2de 100644
--- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -21,6 +21,8 @@
 import com.android.settings.DateTimeSettings;
 import com.android.settings.DisplaySettings;
 import com.android.settings.LegalSettings;
+import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
 import com.android.settings.accessibility.MagnificationPreferenceFragment;
@@ -34,7 +36,7 @@
 import com.android.settings.bluetooth.BluetoothSettings;
 import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
 import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
 import com.android.settings.development.DevelopmentSettingsDashboardFragment;
@@ -167,6 +169,7 @@
         addIndex(PowerUsageSummary.class);
         addIndex(BatterySaverSettings.class);
         addIndex(LockscreenDashboardFragment.class);
+        addIndex(UsbDetailsFragment.class);
         addIndex(WifiDisplaySettings.class);
         addIndex(ZenModeBehaviorSettings.class);
         addIndex(ZenModeAutomationSettings.class);
diff --git a/tests/robotests/src/android/hardware/usb/UsbManagerExtras.java b/tests/robotests/src/android/hardware/usb/UsbManagerExtras.java
new file mode 100644
index 0000000..b9bccd2
--- /dev/null
+++ b/tests/robotests/src/android/hardware/usb/UsbManagerExtras.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 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 android.hardware.usb;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+
+/**
+ * Definitions that were added to UsbManager in P.
+ *
+ * Copied partially from frameworks/base/core/java/android/hardware/usb/UsbManager to
+ * fix issues with roboelectric during test.
+ */
+@SystemService(Context.USB_SERVICE)
+public class UsbManagerExtras {
+    public static final long NONE = 0;
+    public static final long MTP = GadgetFunction.MTP;
+    public static final long PTP = GadgetFunction.PTP;
+    public static final long RNDIS = GadgetFunction.RNDIS;
+    public static final long MIDI = GadgetFunction.MIDI;
+    public static final long ACCESSORY = GadgetFunction.ACCESSORY;
+    public static final long AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+    public static final long ADB = GadgetFunction.ADB;
+
+    private static final long SETTABLE_FUNCTIONS = MTP | PTP | RNDIS | MIDI;
+
+    private static final Map<String, Long> STR_MAP = new HashMap<>();
+
+    static {
+        STR_MAP.put(UsbManager.USB_FUNCTION_MTP, MTP);
+        STR_MAP.put(UsbManager.USB_FUNCTION_PTP, PTP);
+        STR_MAP.put(UsbManager.USB_FUNCTION_RNDIS, RNDIS);
+        STR_MAP.put(UsbManager.USB_FUNCTION_MIDI, MIDI);
+        STR_MAP.put(UsbManager.USB_FUNCTION_ACCESSORY, ACCESSORY);
+        STR_MAP.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, AUDIO_SOURCE);
+        STR_MAP.put(UsbManager.USB_FUNCTION_ADB, ADB);
+    }
+
+    /**
+     * Returns whether the given functions are valid inputs to UsbManager.
+     * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+     */
+    public static boolean isSettableFunctions(long functions) {
+        return (~SETTABLE_FUNCTIONS & functions) == 0;
+    }
+
+    /**
+     * Returns the string representation of the given functions.
+     */
+    public static String usbFunctionsToString(long functions) {
+        StringJoiner joiner = new StringJoiner(",");
+        if ((functions | MTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MTP);
+        }
+        if ((functions | PTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_PTP);
+        }
+        if ((functions | RNDIS) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_RNDIS);
+        }
+        if ((functions | MIDI) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MIDI);
+        }
+        if ((functions | ACCESSORY) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+        }
+        if ((functions | AUDIO_SOURCE) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+        }
+        if ((functions | ADB) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ADB);
+        }
+        return joiner.toString();
+    }
+
+    /**
+     * Parses a string of usb functions and returns a mask of the same functions.
+     */
+    public static long usbFunctionsFromString(String functions) {
+        if (functions == null) {
+            return 0;
+        }
+        long ret = 0;
+        for (String function : functions.split(",")) {
+            if (STR_MAP.containsKey(function)) {
+                ret |= STR_MAP.get(function);
+            }
+        }
+        return ret;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
index 78be742..b478c4e 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
@@ -31,6 +31,7 @@
 
 import com.android.settings.TestConfig;
 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdaterTest.java
similarity index 79%
rename from tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java
rename to tests/robotests/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdaterTest.java
index 16cd3a7..011d620 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdaterTest.java
@@ -13,18 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
-import com.android.settings.deviceinfo.UsbBackend;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -42,6 +44,8 @@
     private ConnectedUsbDeviceUpdater mDeviceUpdater;
 
     @Mock
+    private DashboardFragment mFragment;
+    @Mock
     private UsbConnectionBroadcastReceiver mUsbReceiver;
     @Mock
     private DevicePreferenceCallback mDevicePreferenceCallback;
@@ -53,7 +57,8 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = RuntimeEnvironment.application;
-        mDeviceUpdater = new ConnectedUsbDeviceUpdater(mContext, mDevicePreferenceCallback,
+        when(mFragment.getContext()).thenReturn(mContext);
+        mDeviceUpdater = new ConnectedUsbDeviceUpdater(mFragment, mDevicePreferenceCallback,
                 mUsbBackend);
         mDeviceUpdater.mUsbReceiver = mUsbReceiver;
     }
@@ -70,18 +75,18 @@
 
     @Test
     public void testInitUsbPreference_usbConnected_preferenceAdded() {
-        doReturn(true).when(mUsbReceiver).isConnected();
-
         mDeviceUpdater.initUsbPreference(mContext);
+        mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(true /* connected */,
+                UsbBackend.MODE_DATA_NONE);
 
         verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference);
     }
 
     @Test
     public void testInitUsbPreference_usbDisconnected_preferenceRemoved() {
-        doReturn(false).when(mUsbReceiver).isConnected();
-
         mDeviceUpdater.initUsbPreference(mContext);
+        mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(false /* connected */,
+                UsbBackend.MODE_DATA_NONE);
 
         verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference);
     }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/UsbBackendTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
similarity index 81%
rename from tests/robotests/src/com/android/settings/deviceinfo/UsbBackendTest.java
rename to tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
index ce384a5..40cfd73 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/UsbBackendTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
 
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
 import static org.mockito.Matchers.argThat;
@@ -25,7 +25,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
 
+import com.android.settings.connecteddevice.usb.UsbBackend;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 
@@ -46,6 +48,8 @@
     private UsbManager mUsbManager;
     @Mock
     private UsbBackend.UserRestrictionUtil mUserRestrictionUtil;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
 
     @Before
     public void setUp() {
@@ -53,22 +57,13 @@
         when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI))
             .thenReturn(true);
         when((Object)mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn((Object) mConnectivityManager);
     }
 
     @Test
     public void constructor_noUsbPort_shouldNotCrash() {
-        UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil);
+        UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil, null);
         // Should not crash
     }
-
-    @Test
-    public void getCurrentMode_shouldRegisterReceiverToGetUsbState() {
-        UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil);
-
-        usbBackend.getCurrentMode();
-
-        verify(mContext).registerReceiver(eq(null),
-            argThat(intentFilter -> intentFilter != null &&
-                UsbManager.ACTION_USB_STATE.equals(intentFilter.getAction(0))));
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
similarity index 81%
rename from tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java
rename to tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
index 06bd5b7..50b47e0 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -52,6 +52,8 @@
 
     @Mock
     private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener;
+    @Mock
+    private UsbBackend mUsbBackend;
 
     @Before
     public void setUp() {
@@ -59,27 +61,42 @@
 
         mShadowApplication = ShadowApplication.getInstance();
         mContext = RuntimeEnvironment.application;
-        mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener);
+        mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener, mUsbBackend);
     }
 
     @Test
     public void testOnReceive_usbConnected_invokeCallback() {
         final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_STATE);
         intent.putExtra(UsbManager.USB_CONNECTED, true);
 
         mReceiver.onReceive(mContext, intent);
 
-        verify(mListener).onUsbConnectionChanged(true);
+        verify(mListener).onUsbConnectionChanged(true /* connected */, UsbBackend.MODE_DATA_NONE);
     }
 
     @Test
     public void testOnReceive_usbDisconnected_invokeCallback() {
         final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_STATE);
         intent.putExtra(UsbManager.USB_CONNECTED, false);
 
         mReceiver.onReceive(mContext, intent);
 
-        verify(mListener).onUsbConnectionChanged(false);
+        verify(mListener).onUsbConnectionChanged(false /* connected */, UsbBackend.MODE_DATA_NONE);
+    }
+
+    @Test
+    public void testOnReceive_usbConnectedMtpEnabled_invokeCallback() {
+        final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_STATE);
+        intent.putExtra(UsbManager.USB_CONNECTED, true);
+        intent.putExtra(UsbManager.USB_FUNCTION_MTP, true);
+        intent.putExtra(UsbManager.USB_DATA_UNLOCKED, true);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mListener).onUsbConnectionChanged(true /* connected */, UsbBackend.MODE_DATA_MTP);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java
new file mode 100644
index 0000000..e1f9078
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.PreferenceFragment;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+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,
+        shadows = {ShadowEntityHeaderController.class, SettingsShadowResources.class})
+public class UsbDetailsHeaderControllerTest {
+
+    private UsbDetailsHeaderController mDetailsHeaderController;
+    private Context mContext;
+    private Lifecycle mLifecycle;
+    private LifecycleOwner mLifecycleOwner;
+    private LayoutPreference mPreference;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mScreen;
+
+    @Mock
+    private UsbBackend mUsbBackend;
+    @Mock
+    private PreferenceFragment mFragment;
+    @Mock
+    private Activity mActivity;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private EntityHeaderController mHeaderController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mPreferenceManager = new PreferenceManager(mContext);
+        mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+
+        when(mFragment.getActivity()).thenReturn(mActivity);
+        when(mActivity.getApplicationContext()).thenReturn(mContext);
+        when(mFragment.getContext()).thenReturn(mContext);
+        when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        ShadowEntityHeaderController.setUseMock(mHeaderController);
+        mDetailsHeaderController = new UsbDetailsHeaderController(mContext, mFragment, mUsbBackend);
+        mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+        mPreference.setKey(mDetailsHeaderController.getPreferenceKey());
+        mScreen.addPreference(mPreference);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowEntityHeaderController.reset();
+    }
+
+    @Test
+    public void displayRefresh_charging_shouldSetHeader() {
+        mDetailsHeaderController.displayPreference(mScreen);
+        mDetailsHeaderController.refresh(UsbBackend.MODE_DATA_NONE);
+        verify(mHeaderController).setLabel(mContext.getString(R.string.usb_pref));
+        verify(mHeaderController).setIcon(mContext.getDrawable(R.drawable.ic_usb));
+        verify(mHeaderController).setSummary(
+                mContext.getString(R.string.usb_summary_charging_only));
+        verify(mHeaderController).done(mActivity, true);
+    }
+
+    @Test
+    public void displayRefresh_mtp_shouldSetHeader() {
+        mDetailsHeaderController.displayPreference(mScreen);
+        mDetailsHeaderController.refresh(UsbBackend.MODE_DATA_MTP);
+        verify(mHeaderController).setLabel(mContext.getString(R.string.usb_pref));
+        verify(mHeaderController).setIcon(mContext.getDrawable(R.drawable.ic_usb));
+        verify(mHeaderController).setSummary(
+                mContext.getString(R.string.usb_summary_file_transfers));
+        verify(mHeaderController).done(mActivity, true);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java
new file mode 100644
index 0000000..557d836
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import com.google.android.collect.Lists;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+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 UsbDetailsProfilesControllerTest {
+
+    private UsbDetailsProfilesController mDetailsProfilesController;
+    private Context mContext;
+    private Lifecycle mLifecycle;
+    private PreferenceCategory mPreference;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mScreen;
+    private List<String> mOptions;
+
+    @Mock
+    private UsbBackend mUsbBackend;
+    @Mock
+    private PreferenceFragment mFragment;
+    @Mock
+    private Activity mActivity;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mLifecycle = new Lifecycle(() -> mLifecycle);
+        mPreferenceManager = new PreferenceManager(mContext);
+        mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+
+        when(mFragment.getActivity()).thenReturn(mActivity);
+        when(mActivity.getApplicationContext()).thenReturn(mContext);
+        when(mFragment.getContext()).thenReturn(mContext);
+        when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        mOptions = Lists.newArrayList(UsbManager.USB_FUNCTION_MTP, UsbManager.USB_FUNCTION_PTP,
+                UsbManager.USB_FUNCTION_MIDI, UsbDetailsProfilesController.KEY_POWER);
+        mDetailsProfilesController = new UsbDetailsProfilesController(mContext, mFragment,
+                mUsbBackend, mOptions, "usb_options");
+        mPreference = new PreferenceCategory(mContext);
+        mPreference.setKey(mDetailsProfilesController.getPreferenceKey());
+        mScreen.addPreference(mPreference);
+    }
+
+    @Test
+    public void testDisplayRefresh_allAllowed_shouldCreateSwitches() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+        List<SwitchPreference> switches = getProfileSwitches();
+
+        for (int i = 0; i < switches.size(); i++) {
+            assertThat(switches.get(i).getKey().equals(mOptions.get(i)));
+        }
+    }
+
+    @Test
+    public void testDisplayRefresh_onlyMidiAllowed_shouldCreateOnlyMidiSwitch() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_MIDI)).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_MTP)).thenReturn(true);
+        when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_PTP)).thenReturn(true);
+        when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_POWER_SOURCE)).thenReturn(true);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+        List<SwitchPreference> switches = getProfileSwitches();
+        assertThat(switches.size()).isEqualTo(1);
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MIDI);
+    }
+
+    @Test
+    public void testDisplayRefresh_mtpEnabled_shouldCheckSwitches() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP);
+        List<SwitchPreference> switches = getProfileSwitches();
+
+        assertThat(switches.get(0).getKey().equals(UsbManager.USB_FUNCTION_MTP));
+        assertThat(switches.get(0).isChecked());
+    }
+
+    @Test
+    public void testDisplayRefresh_mtpSupplyPowerEnabled_shouldCheckSwitches() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP | UsbBackend.MODE_POWER_SOURCE);
+        List<SwitchPreference> switches = getProfileSwitches();
+
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+        assertThat(switches.get(0).isChecked());
+        assertThat(switches.get(3).getKey()).isEqualTo(UsbDetailsProfilesController.KEY_POWER);
+        assertThat(switches.get(3).isChecked());
+    }
+
+    @Test
+    public void testOnClickMtp_noneEnabled_shouldEnableMtp() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+        List<SwitchPreference> switches = getProfileSwitches();
+        switches.get(0).performClick();
+
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+        verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP);
+        assertThat(switches.get(0).isChecked());
+    }
+
+    @Test
+    public void testOnClickMtp_supplyingPowerEnabled_shouldEnableBoth() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_POWER_SOURCE);
+        when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SOURCE);
+        List<SwitchPreference> switches = getProfileSwitches();
+        switches.get(0).performClick();
+
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+        verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP | UsbBackend.MODE_POWER_SOURCE);
+        assertThat(switches.get(0).isChecked());
+        assertThat(switches.get(3).getKey()).isEqualTo(UsbDetailsProfilesController.KEY_POWER);
+        assertThat(switches.get(3).isChecked());
+    }
+
+    @Test
+    public void testOnClickMtp_ptpEnabled_shouldEnableMtpOnly() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_PTP);
+        when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_DATA_PTP);
+        List<SwitchPreference> switches = getProfileSwitches();
+        switches.get(0).performClick();
+
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+        verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP);
+        assertThat(switches.get(0).isChecked());
+        assertThat(switches.get(1).getKey()).isEqualTo(UsbManager.USB_FUNCTION_PTP);
+        assertThat(!switches.get(1).isChecked());
+    }
+
+    @Test
+    public void testOnClickMtp_mtpEnabled_shouldDisableMtp() {
+        when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+        when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+        when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+        mDetailsProfilesController.displayPreference(mScreen);
+        mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP);
+        when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_DATA_MTP);
+        List<SwitchPreference> switches = getProfileSwitches();
+        switches.get(0).performClick();
+
+        assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+        verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_NONE);
+        assertThat(!switches.get(0).isChecked());
+    }
+
+    private List<SwitchPreference> getProfileSwitches() {
+        ArrayList<SwitchPreference> result = new ArrayList<>();
+        for (int i = 0; i < mPreference.getPreferenceCount(); i++) {
+            result.add((SwitchPreference) mPreference.getPreference(i));
+        }
+        return result;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/UsbModeChooserActivityTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivityTest.java
similarity index 96%
rename from tests/robotests/src/com/android/settings/deviceinfo/UsbModeChooserActivityTest.java
rename to tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivityTest.java
index 1817bfb..c02212b 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/UsbModeChooserActivityTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivityTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.anyInt;
@@ -22,6 +22,7 @@
 
 import android.widget.TextView;
 import com.android.settings.R;
+import com.android.settings.connecteddevice.usb.UsbModeChooserActivity;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModePreferenceControllerTest.java
similarity index 67%
rename from tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java
rename to tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModePreferenceControllerTest.java
index 7edde6e..d15a57f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModePreferenceControllerTest.java
@@ -1,16 +1,12 @@
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
 
 import android.content.Context;
-import android.content.Intent;
-import android.hardware.usb.UsbManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.deviceinfo.UsbBackend;
-import com.android.settings.deviceinfo.UsbModeChooserActivity;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -24,6 +20,7 @@
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -33,6 +30,8 @@
     private UsbBackend mUsbBackend;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private PreferenceScreen mScreen;
+    @Mock
+    private UsbConnectionBroadcastReceiver mUsbConnectionBroadcastReceiver;
 
     private Context mContext;
     private UsbModePreferenceController mController;
@@ -42,61 +41,67 @@
         MockitoAnnotations.initMocks(this);
         mContext = ShadowApplication.getInstance().getApplicationContext();
         mController = new UsbModePreferenceController(mContext, mUsbBackend);
+        mController.mUsbReceiver = mUsbConnectionBroadcastReceiver;
     }
 
     @Test
     public void testGetSummary_chargeDevice() {
-        assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[0]))
+        assertThat(mController.getSummary(0))
                 .isEqualTo(R.string.usb_summary_charging_only);
     }
 
     @Test
     public void testGetSummary_supplyPower() {
-        assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[1]))
+        assertThat(mController.getSummary(UsbBackend.MODE_POWER_SOURCE))
                 .isEqualTo(R.string.usb_summary_power_only);
     }
 
     @Test
     public void testGetSummary_TransferFiles() {
-        assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[2]))
+        assertThat(mController.getSummary(UsbBackend.MODE_DATA_MTP))
                 .isEqualTo(R.string.usb_summary_file_transfers);
     }
 
     @Test
     public void testGetSummary_TransferPhoto() {
-        assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[3]))
+        assertThat(mController.getSummary(UsbBackend.MODE_DATA_PTP))
                 .isEqualTo(R.string.usb_summary_photo_transfers);
     }
 
     @Test
     public void testGetSummary_MIDI() {
-        assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[4]))
+        assertThat(mController.getSummary(UsbBackend.MODE_DATA_MIDI))
                 .isEqualTo(R.string.usb_summary_MIDI);
     }
 
     @Test
+    public void testGetSummary_Tethering() {
+        assertThat(mController.getSummary(UsbBackend.MODE_DATA_TETHER))
+                .isEqualTo(R.string.usb_summary_tether);
+    }
+
+    @Test
     public void testPreferenceSummary_usbDisconnected() {
         final Preference preference = new Preference(mContext);
         preference.setKey("usb_mode");
         preference.setEnabled(true);
+        when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
+        when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(false);
         mController.updateState(preference);
+
+        assertThat(preference.getKey()).isEqualTo("usb_mode");
         assertThat(preference.getSummary()).isEqualTo(
                 mContext.getString(R.string.disconnected));
     }
 
     @Test
-    public void testUsbBoradcastReceiver_usbConnected_shouldUpdateSummary() {
+    public void testUsbBroadcastReceiver_usbConnected_shouldUpdateSummary() {
         final Preference preference = new Preference(mContext);
         preference.setKey("usb_mode");
         preference.setEnabled(true);
-        when(mUsbBackend.getCurrentMode()).thenReturn(UsbModeChooserActivity.DEFAULT_MODES[0]);
-        when(mScreen.findPreference("usb_mode")).thenReturn(preference);
-
-        mController.displayPreference(mScreen);
-        mController.onResume();
-        final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
-        intent.putExtra(UsbManager.USB_CONNECTED, true);
-        mContext.sendStickyBroadcast(intent);
+        when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
+        when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(true);
+        mController.updateState(preference);
 
         assertThat(preference.getSummary()).isEqualTo(
                 mContext.getString(R.string.usb_summary_charging_only));
diff --git a/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java
index 8719bb4..67a6d6b 100644
--- a/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java
@@ -25,6 +25,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -37,11 +38,13 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbManagerExtras;
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.connecteddevice.usb.UsbBackend;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -69,6 +72,8 @@
     private UsbManager mUsbManager;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
 
     private Context mContext;
     private LifecycleOwner mLifecycleOwner;
@@ -101,6 +106,13 @@
         mController = spy(new SelectUsbConfigPreferenceController(mContext, mLifecycle));
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
         mController.displayPreference(mScreen);
+        mController.mUsbManagerPassThrough = mUsbManagerPassThrough;
+
+        when(mUsbManagerPassThrough.usbFunctionsFromString("mtp")).thenReturn(UsbManagerExtras.MTP);
+        when(mUsbManagerPassThrough.usbFunctionsFromString("rndis"))
+                .thenReturn(UsbManagerExtras.RNDIS);
+        when(mUsbManagerPassThrough.usbFunctionsFromString("none"))
+                .thenReturn(UsbManagerExtras.NONE);
 
     }
 
@@ -111,11 +123,13 @@
 
     @Test
     public void onPreferenceChange_setCharging_shouldEnableCharging() {
-        when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true);
-        doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+        when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(
+                UsbManagerExtras.usbFunctionsFromString(mValues[0]));
+        doNothing().when(mController).setCurrentFunctions(anyLong());
         mController.onPreferenceChange(mPreference, mValues[0]);
 
-        verify(mController).setCurrentFunction(mValues[0], false /* usb data unlock */);
+        verify(mController).setCurrentFunctions(
+                UsbManagerExtras.usbFunctionsFromString(mValues[0]));
     }
 
     @Test
@@ -144,28 +158,32 @@
 
     @Test
     public void onPreferenceChange_setMtp_shouldEnableMtp() {
-        when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true);
-        doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+        when(mUsbManagerPassThrough.getCurrentFunctions())
+                .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
+        doNothing().when(mController).setCurrentFunctions(anyLong());
         mController.onPreferenceChange(mPreference, mValues[1]);
 
-        verify(mController).setCurrentFunction(mValues[1], true /* usb data unlock */);
+        verify(mController).setCurrentFunctions(
+                UsbManagerExtras.usbFunctionsFromString(mValues[1]));
     }
 
     @Test
     public void onPreferenceChange_monkeyUser_shouldReturnFalse() {
-        when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true);
+        when(mUsbManagerPassThrough.getCurrentFunctions())
+                .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
         ShadowUtils.setIsUserAMonkey(true);
-        doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+        doNothing().when(mController).setCurrentFunctions(anyLong());
 
         final boolean isHandled = mController.onPreferenceChange(mPreference, mValues[1]);
 
         assertThat(isHandled).isFalse();
-        verify(mController, never()).setCurrentFunction(any(), anyBoolean());
+        verify(mController, never()).setCurrentFunctions(anyLong());
     }
 
     @Test
     public void updateState_chargingEnabled_shouldSetPreferenceToCharging() {
-        when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true);
+        when(mUsbManagerPassThrough.getCurrentFunctions())
+                .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[0]));
 
         mController.updateState(mPreference);
 
@@ -175,7 +193,8 @@
 
     @Test
     public void updateState_RndisEnabled_shouldEnableRndis() {
-        when(mUsbManager.isFunctionEnabled(mValues[3])).thenReturn(true);
+        when(mUsbManagerPassThrough.getCurrentFunctions())
+                .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[3]));
 
         mController.updateState(mPreference);
 
@@ -185,6 +204,7 @@
 
     @Test
     public void updateState_noValueSet_shouldEnableChargingAsDefault() {
+        when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(UsbManagerExtras.NONE);
         mController.updateState(mPreference);
 
         verify(mPreference).setValue(mValues[0]);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowConnectivityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowConnectivityManager.java
index 742fbf8..fc19b44 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowConnectivityManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowConnectivityManager.java
@@ -26,6 +26,7 @@
 public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
 
     private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
+    private boolean mTetheringSupported = false;
 
     public void setNetworkSupported(int networkType, boolean supported) {
         mSupportedNetworkTypes.put(networkType, supported);
@@ -35,4 +36,13 @@
     public boolean isNetworkSupported(int networkType) {
         return mSupportedNetworkTypes.get(networkType);
     }
+
+    public void setTetheringSupported(boolean supported) {
+        mTetheringSupported = supported;
+    }
+
+    @Implementation
+    public boolean isTetheringSupported() {
+        return mTetheringSupported;
+    }
 }