Add new USB details screen for Connected Devices 2.0

Also updated UsbBackend to use the new UsbFunctions
api.

Added new unit tests for UsbDetailsHeaderController
and UsbDetailsProfilesController.

Bug: 69333961
Test: make RunSettingsRoboTests
Change-Id: I133750190bb61dfe0e20b06f50e50ea13b347f1e
This commit is contained in:
Jerry Zhang
2018-01-11 16:47:14 -08:00
parent 85fd529cc4
commit d18d6f1022
34 changed files with 1342 additions and 243 deletions

View File

@@ -2012,13 +2012,21 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".deviceinfo.UsbModeChooserActivity" <activity android:name=".connecteddevice.usb.UsbModeChooserActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="true" android:exported="true"
android:permission="android.permission.MANAGE_USB" android:permission="android.permission.MANAGE_USB"
android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"> android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar">
</activity> </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" <activity android:name=".RemoteBugreportActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="true" android:exported="true"

View File

@@ -8000,15 +8000,15 @@
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user <!-- 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 select what the USB connection for this device should be used for. This choice
is for powering the other device only. --> 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 <!-- 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 user select what the USB connection for this device should be used for. This choice
is for powering the other device. --> 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 <!-- 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 select what the USB connection for this device should be used for. This choice
is for transferring files via MTP. --> 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 <!-- 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 select what the USB connection for this device should be used for. This choice
is for transferring files via MTP. --> is for transferring files via MTP. -->
@@ -8016,23 +8016,31 @@
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user <!-- 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 select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. --> 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 <!-- 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 select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. --> is for transferring photos via PTP. -->
<string name="usb_use_photo_transfers_desc">Transfer photos or files if MTP is not supported (PTP)</string> <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 <!-- 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 select what the USB connection for this device should be used for. This choice
is for entering MIDI mode. --> 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 <!-- 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 select what the USB connection for this device should be used for. This choice
is for entering MIDI mode. --> is for entering MIDI mode. -->
<string name="usb_use_MIDI_desc">Use this device as MIDI</string> <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 <!-- 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, for this device should be used for. These options are more commonly used.
usb_use_file_transfer, use_use_photo_transfer, and usb_use_MIDI --> Choices are usb_use_file_transfer.-->
<string name="usb_use">Use USB to</string> <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] --> <!-- Settings item title for USB preference [CHAR LIMIT=35] -->
<string name="usb_pref">USB</string> <string name="usb_pref">USB</string>
@@ -8040,13 +8048,27 @@
<!-- Settings item summary for USB preference when set to charging only [CHAR LIMIT=NONE] --> <!-- Settings item summary for USB preference when set to charging only [CHAR LIMIT=NONE] -->
<string name="usb_summary_charging_only">Charging this device</string> <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] --> <!-- 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] --> <!-- 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] --> <!-- 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] --> <!-- 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] --> <!-- Settings item title for SMS Mirroring preference [CHAR LIMIT=35] -->
<string name="sms_mirroring_pref">SMS Mirroring</string> <string name="sms_mirroring_pref">SMS Mirroring</string>

View File

@@ -56,16 +56,6 @@
android:summary="@string/bluetooth_on_while_driving_summary" android:summary="@string/bluetooth_on_while_driving_summary"
android:order="-2"/> 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 <Preference
android:key="bt_received_files" android:key="bt_received_files"
android:icon="@drawable/ic_folder_vd_theme_24" android:icon="@drawable/ic_folder_vd_theme_24"

View File

@@ -53,7 +53,7 @@
android:order="-2"> android:order="-2">
<intent android:action="android.intent.action.MAIN" <intent android:action="android.intent.action.MAIN"
android:targetPackage="com.android.settings" android:targetPackage="com.android.settings"
android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/> android:targetClass="com.android.settings.connecteddevice.usb.UsbModeChooserActivity"/>
</Preference> </Preference>
<PreferenceCategory <PreferenceCategory

View File

@@ -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>

View File

@@ -96,6 +96,7 @@ public class Settings extends SettingsActivity {
public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ } public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
public static class UsbSettingsActivity 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 TrustedCredentialsSettingsActivity extends SettingsActivity { /* empty */ }
public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ } public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrintSettingsActivity extends SettingsActivity { /* empty */ } public static class PrintSettingsActivity extends SettingsActivity { /* empty */ }

View File

@@ -24,8 +24,9 @@ import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothFilesPreferenceController; import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController; import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
import com.android.settings.bluetooth.BluetoothSwitchPreferenceController; 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.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.UsbBackend;
import com.android.settings.nfc.NfcPreferenceController; import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;

View File

@@ -26,9 +26,10 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController; import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
import com.android.settings.bluetooth.Utils; 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.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader; import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.deviceinfo.UsbBackend;
import com.android.settings.nfc.NfcPreferenceController; import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;

View File

@@ -20,6 +20,7 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.BluetoothDeviceUpdater;
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
@@ -48,7 +49,7 @@ public class ConnectedDeviceGroupController extends AbstractPreferenceController
public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) { public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
super(fragment.getContext()); super(fragment.getContext());
init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this), init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this),
new ConnectedUsbDeviceUpdater(fragment.getContext(), this)); new ConnectedUsbDeviceUpdater(fragment, this));
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -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);
}
}

View File

@@ -13,22 +13,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.connecteddevice; package com.android.settings.connecteddevice.usb;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.os.Bundle;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.PreferenceFragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.deviceinfo.UsbBackend; import com.android.settings.SettingsActivity;
import com.android.settings.deviceinfo.UsbModeChooserActivity; import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.GearPreference; import com.android.settings.widget.GearPreference;
/** /**
* Controller to maintain connected usb device * Controller to maintain connected usb device
*/ */
public class ConnectedUsbDeviceUpdater { public class ConnectedUsbDeviceUpdater {
private Context mContext; private PreferenceFragment mFragment;
private UsbBackend mUsbBackend; private UsbBackend mUsbBackend;
private DevicePreferenceCallback mDevicePreferenceCallback; private DevicePreferenceCallback mDevicePreferenceCallback;
@VisibleForTesting @VisibleForTesting
@@ -36,8 +38,9 @@ public class ConnectedUsbDeviceUpdater {
@VisibleForTesting @VisibleForTesting
UsbConnectionBroadcastReceiver mUsbReceiver; UsbConnectionBroadcastReceiver mUsbReceiver;
private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener = @VisibleForTesting
(connected) -> { UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
(connected, newMode) -> {
if (connected) { if (connected) {
mUsbPreference.setSummary( mUsbPreference.setSummary(
UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode())); UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode()));
@@ -47,18 +50,19 @@ public class ConnectedUsbDeviceUpdater {
} }
}; };
public ConnectedUsbDeviceUpdater(Context context, public ConnectedUsbDeviceUpdater(DashboardFragment fragment,
DevicePreferenceCallback devicePreferenceCallback) { DevicePreferenceCallback devicePreferenceCallback) {
this(context, devicePreferenceCallback, new UsbBackend(context)); this(fragment, devicePreferenceCallback, new UsbBackend(fragment.getContext()));
} }
@VisibleForTesting @VisibleForTesting
ConnectedUsbDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback, ConnectedUsbDeviceUpdater(DashboardFragment fragment,
UsbBackend usbBackend) { DevicePreferenceCallback devicePreferenceCallback, UsbBackend usbBackend) {
mContext = context; mFragment = fragment;
mDevicePreferenceCallback = devicePreferenceCallback; mDevicePreferenceCallback = devicePreferenceCallback;
mUsbBackend = usbBackend; mUsbBackend = usbBackend;
mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener); mUsbReceiver = new UsbConnectionBroadcastReceiver(fragment.getContext(),
mUsbConnectionListener, mUsbBackend);
} }
public void registerCallback() { public void registerCallback() {
@@ -76,8 +80,12 @@ public class ConnectedUsbDeviceUpdater {
mUsbPreference.setIcon(R.drawable.ic_usb); mUsbPreference.setIcon(R.drawable.ic_usb);
mUsbPreference.setSelectable(false); mUsbPreference.setSelectable(false);
mUsbPreference.setOnGearClickListener((GearPreference p) -> { mUsbPreference.setOnGearClickListener((GearPreference p) -> {
final Intent intent = new Intent(mContext, UsbModeChooserActivity.class); // New version - uses a separate screen.
mContext.startActivity(intent); 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(); forceUpdate();
@@ -87,6 +95,5 @@ public class ConnectedUsbDeviceUpdater {
// Register so we can get the connection state from sticky intent. // Register so we can get the connection state from sticky intent.
//TODO(b/70336520): Use an API to get data instead of sticky intent //TODO(b/70336520): Use an API to get data instead of sticky intent
mUsbReceiver.register(); mUsbReceiver.register();
mUsbConnectionListener.onUsbConnectionChanged(mUsbReceiver.isConnected());
} }
} }

View File

@@ -0,0 +1,3 @@
# Default reviewers for this and subdirectories.
zhangjerry@google.com
badhri@google.com

View File

@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.deviceinfo; package com.android.settings.connecteddevice.usb;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus; import android.hardware.usb.UsbPortStatus;
import android.net.ConnectivityManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
@@ -32,34 +31,52 @@ public class UsbBackend {
public static final int MODE_POWER_SINK = 0x00; public static final int MODE_POWER_SINK = 0x00;
public static final int MODE_POWER_SOURCE = 0x01; public static final int MODE_POWER_SOURCE = 0x01;
public static final int MODE_DATA_MASK = 0x03 << 1; public static final int MODE_DATA_MASK = 0x0f << 1;
public static final int MODE_DATA_NONE = 0x00 << 1; public static final int MODE_DATA_NONE = 0;
public static final int MODE_DATA_MTP = 0x01 << 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_PTP = 0x01 << 2;
public static final int MODE_DATA_MIDI = 0x03 << 1; public static final int MODE_DATA_MIDI = 0x01 << 3;
public static final int MODE_DATA_TETHER = 0x01 << 4;
private final boolean mRestricted; private final boolean mFileTransferRestricted;
private final boolean mRestrictedBySystem; private final boolean mFileTransferRestrictedBySystem;
private final boolean mMidi; private final boolean mTetheringRestricted;
private final boolean mTetheringRestrictedBySystem;
private final boolean mMidiSupported;
private final boolean mTetheringSupported;
private UsbManager mUsbManager; private UsbManager mUsbManager;
@VisibleForTesting
UsbManagerPassThrough mUsbManagerPassThrough;
private UsbPort mPort; private UsbPort mPort;
private UsbPortStatus mPortStatus; private UsbPortStatus mPortStatus;
private Context mContext; private Context mContext;
public UsbBackend(Context context) { public UsbBackend(Context context) {
this(context, new UserRestrictionUtil(context)); this(context, new UserRestrictionUtil(context), null);
} }
@VisibleForTesting @VisibleForTesting
public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil) { public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil,
UsbManagerPassThrough usbManagerPassThrough) {
mContext = context; mContext = context;
mUsbManager = context.getSystemService(UsbManager.class); mUsbManager = context.getSystemService(UsbManager.class);
mRestricted = userRestrictionUtil.isUsbFileTransferRestricted(); mUsbManagerPassThrough = usbManagerPassThrough;
mRestrictedBySystem = userRestrictionUtil.isUsbFileTransferRestrictedBySystem(); if (mUsbManagerPassThrough == null) {
mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 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(); UsbPort[] ports = mUsbManager.getPorts();
if (ports == null) { if (ports == null) {
@@ -81,6 +98,7 @@ public class UsbBackend {
public int getCurrentMode() { public int getCurrentMode() {
if (mPort != null) { if (mPort != null) {
int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
&& mPortStatus.isConnected()
? MODE_POWER_SOURCE : MODE_POWER_SINK; ? MODE_POWER_SOURCE : MODE_POWER_SINK;
return power | getUsbDataMode(); return power | getUsbDataMode();
} }
@@ -88,38 +106,35 @@ public class UsbBackend {
} }
public int getUsbDataMode() { public int getUsbDataMode() {
if (!isUsbDataUnlocked()) { long functions = mUsbManagerPassThrough.getCurrentFunctions();
return MODE_DATA_NONE; if (functions == UsbManager.FUNCTION_MTP) {
} else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
return MODE_DATA_MTP; return MODE_DATA_MTP;
} else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) { } else if (functions == UsbManager.FUNCTION_PTP) {
return MODE_DATA_PTP; return MODE_DATA_PTP;
} else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) { } else if (functions == UsbManager.FUNCTION_MIDI) {
return MODE_DATA_MIDI; return MODE_DATA_MIDI;
} else if (functions == UsbManager.FUNCTION_RNDIS) {
return MODE_DATA_TETHER;
} }
return MODE_DATA_NONE; // ... 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) { private void setUsbFunction(int mode) {
switch (mode) { switch (mode) {
case MODE_DATA_MTP: case MODE_DATA_MTP:
mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true); mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MTP);
break; break;
case MODE_DATA_PTP: case MODE_DATA_PTP:
mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true); mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_PTP);
break; break;
case MODE_DATA_MIDI: case MODE_DATA_MIDI:
mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI, true); mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MIDI);
break;
case MODE_DATA_TETHER:
mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
break; break;
default: default:
mUsbManager.setCurrentFunction(null, false); mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_NONE);
break; break;
} }
} }
@@ -144,28 +159,32 @@ public class UsbBackend {
} }
public boolean isModeDisallowed(int mode) { public boolean isModeDisallowed(int mode) {
if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE if (mFileTransferRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
&& (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
// No USB data modes are supported. return true;
} else if (mTetheringRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
return true; return true;
} }
return false; return false;
} }
public boolean isModeDisallowedBySystem(int mode) { public boolean isModeDisallowedBySystem(int mode) {
if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE if (mFileTransferRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
&& (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
// No USB data modes are supported. return true;
} else if (mTetheringRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
return true; return true;
} }
return false; return false;
} }
public boolean isModeSupported(int mode) { public boolean isModeSupported(int mode) {
if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) { if (!mMidiSupported && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
return false; return false;
} }
if (!mTetheringSupported && (mode & MODE_DATA_MASK) == MODE_DATA_TETHER) {
return false;
}
if (mPort != null) { if (mPort != null) {
int power = modeToPower(mode); int power = modeToPower(mode);
if ((mode & MODE_DATA_MASK) != 0) { if ((mode & MODE_DATA_MASK) != 0) {
@@ -194,9 +213,35 @@ public class UsbBackend {
return mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); return mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
} }
public boolean isUsbTetheringRestricted() {
return mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
}
public boolean isUsbFileTransferRestrictedBySystem() { public boolean isUsbFileTransferRestrictedBySystem() {
return mUserManager.hasBaseUserRestriction( return mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId())); 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);
}
} }
} }

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
};
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.deviceinfo; package com.android.settings.connecteddevice.usb;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity; import android.app.Activity;

View File

@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.connecteddevice; package com.android.settings.connecteddevice.usb;
import android.content.Context; import android.content.Context;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.deviceinfo.UsbBackend;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -33,27 +33,27 @@ public class UsbModePreferenceController extends AbstractPreferenceController
private static final String KEY_USB_MODE = "usb_mode"; private static final String KEY_USB_MODE = "usb_mode";
private UsbBackend mUsbBackend; private UsbBackend mUsbBackend;
private UsbConnectionBroadcastReceiver mUsbReceiver; @VisibleForTesting
UsbConnectionBroadcastReceiver mUsbReceiver;
private Preference mUsbPreference; private Preference mUsbPreference;
public UsbModePreferenceController(Context context, UsbBackend usbBackend) { public UsbModePreferenceController(Context context, UsbBackend usbBackend) {
super(context); super(context);
mUsbBackend = usbBackend; mUsbBackend = usbBackend;
mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected) -> { mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected, newMode) -> {
updateSummary(mUsbPreference); updateSummary(mUsbPreference, connected, newMode);
}); }, mUsbBackend);
} }
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
mUsbPreference = screen.findPreference(KEY_USB_MODE); mUsbPreference = screen.findPreference(KEY_USB_MODE);
updateSummary(mUsbPreference);
} }
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
updateSummary(preference); updateSummary(preference, mUsbReceiver.isConnected(), mUsbBackend.getCurrentMode());
} }
@Override @Override
@@ -88,17 +88,24 @@ public class UsbModePreferenceController extends AbstractPreferenceController
return R.string.usb_summary_photo_transfers; return R.string.usb_summary_photo_transfers;
case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI: case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI:
return R.string.usb_summary_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) { private void updateSummary(Preference preference, boolean connected, int mode) {
updateSummary(preference, mUsbBackend.getCurrentMode());
}
private void updateSummary(Preference preference, int mode) {
if (preference != null) { if (preference != null) {
if (mUsbReceiver.isConnected()) { if (connected) {
preference.setEnabled(true); preference.setEnabled(true);
preference.setSummary(getSummary(mode)); preference.setSummary(getSummary(mode));
} else { } else {
@@ -107,5 +114,4 @@ public class UsbModePreferenceController extends AbstractPreferenceController
} }
} }
} }
} }

View File

@@ -60,6 +60,7 @@ import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
import com.android.settings.datausage.DataPlanUsageSummary; import com.android.settings.datausage.DataPlanUsageSummary;
import com.android.settings.datausage.DataUsageList; import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary; import com.android.settings.datausage.DataUsageSummary;
@@ -246,6 +247,7 @@ public class SettingsGateway {
NetworkDashboardFragment.class.getName(), NetworkDashboardFragment.class.getName(),
ConnectedDeviceDashboardFragment.class.getName(), ConnectedDeviceDashboardFragment.class.getName(),
ConnectedDeviceDashboardFragmentOld.class.getName(), ConnectedDeviceDashboardFragmentOld.class.getName(),
UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(), AppAndNotificationDashboardFragment.class.getName(),
AccountDashboardFragment.class.getName(), AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(), EnterprisePrivacySettings.class.getName(),

View File

@@ -27,11 +27,11 @@ import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.ListPreference; import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin; 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.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate; import com.android.settingslib.core.lifecycle.events.OnCreate;
@@ -48,6 +48,8 @@ public class SelectUsbConfigPreferenceController extends
private final String[] mListValues; private final String[] mListValues;
private final String[] mListSummaries; private final String[] mListSummaries;
private final UsbManager mUsbManager; private final UsbManager mUsbManager;
@VisibleForTesting
UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
private BroadcastReceiver mUsbReceiver; private BroadcastReceiver mUsbReceiver;
private ListPreference mPreference; private ListPreference mPreference;
@@ -57,6 +59,7 @@ public class SelectUsbConfigPreferenceController extends
mListValues = context.getResources().getStringArray(R.array.usb_configuration_values); mListValues = context.getResources().getStringArray(R.array.usb_configuration_values);
mListSummaries = context.getResources().getStringArray(R.array.usb_configuration_titles); mListSummaries = context.getResources().getStringArray(R.array.usb_configuration_titles);
mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
mUsbManagerPassThrough = new UsbBackend.UsbManagerPassThrough(mUsbManager);
mUsbReceiver = new BroadcastReceiver() { mUsbReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@@ -95,7 +98,8 @@ public class SelectUsbConfigPreferenceController extends
return false; return false;
} }
writeUsbConfigurationOption(newValue.toString()); writeUsbConfigurationOption(mUsbManagerPassThrough
.usbFunctionsFromString(newValue.toString()));
updateUsbConfigurationValues(); updateUsbConfigurationValues();
return true; return true;
} }
@@ -129,14 +133,15 @@ public class SelectUsbConfigPreferenceController extends
} }
@VisibleForTesting @VisibleForTesting
void setCurrentFunction(String newValue, boolean usbDataUnlocked) { void setCurrentFunctions(long functions) {
mUsbManager.setCurrentFunction(newValue, usbDataUnlocked); mUsbManager.setCurrentFunctions(functions);
} }
private void updateUsbConfigurationValues() { private void updateUsbConfigurationValues() {
long functions = mUsbManagerPassThrough.getCurrentFunctions();
int index = 0; int index = 0;
for (int i = 0; i < mListValues.length; i++) { for (int i = 0; i < mListValues.length; i++) {
if (mUsbManager.isFunctionEnabled(mListValues[i])) { if (functions == mUsbManagerPassThrough.usbFunctionsFromString(mListValues[i])) {
index = i; index = i;
break; break;
} }
@@ -145,11 +150,7 @@ public class SelectUsbConfigPreferenceController extends
mPreference.setSummary(mListSummaries[index]); mPreference.setSummary(mListSummaries[index]);
} }
private void writeUsbConfigurationOption(String newValue) { private void writeUsbConfigurationOption(long newValue) {
if (TextUtils.equals(newValue, "none")) { setCurrentFunctions(newValue);
setCurrentFunction(newValue, false);
} else {
setCurrentFunction(newValue, true);
}
} }
} }

View File

@@ -21,6 +21,8 @@ import android.support.annotation.VisibleForTesting;
import com.android.settings.DateTimeSettings; import com.android.settings.DateTimeSettings;
import com.android.settings.DisplaySettings; import com.android.settings.DisplaySettings;
import com.android.settings.LegalSettings; 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.AccessibilitySettings;
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment; import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
import com.android.settings.accessibility.MagnificationPreferenceFragment; import com.android.settings.accessibility.MagnificationPreferenceFragment;
@@ -34,7 +36,7 @@ import com.android.settings.backup.BackupSettingsFragment;
import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; 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.datausage.DataUsageSummary;
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings; import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
import com.android.settings.development.DevelopmentSettingsDashboardFragment; import com.android.settings.development.DevelopmentSettingsDashboardFragment;
@@ -167,6 +169,7 @@ public class SearchIndexableResourcesImpl implements SearchIndexableResources {
addIndex(PowerUsageSummary.class); addIndex(PowerUsageSummary.class);
addIndex(BatterySaverSettings.class); addIndex(BatterySaverSettings.class);
addIndex(LockscreenDashboardFragment.class); addIndex(LockscreenDashboardFragment.class);
addIndex(UsbDetailsFragment.class);
addIndex(WifiDisplaySettings.class); addIndex(WifiDisplaySettings.class);
addIndex(ZenModeBehaviorSettings.class); addIndex(ZenModeBehaviorSettings.class);
addIndex(ZenModeAutomationSettings.class); addIndex(ZenModeAutomationSettings.class);

View File

@@ -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;
}
}

View File

@@ -31,6 +31,7 @@ import android.support.v7.preference.PreferenceScreen;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;

View File

@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License * 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.assertThat;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.TestConfig; 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 com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before; import org.junit.Before;
@@ -41,6 +43,8 @@ public class ConnectedUsbDeviceUpdaterTest {
private Context mContext; private Context mContext;
private ConnectedUsbDeviceUpdater mDeviceUpdater; private ConnectedUsbDeviceUpdater mDeviceUpdater;
@Mock
private DashboardFragment mFragment;
@Mock @Mock
private UsbConnectionBroadcastReceiver mUsbReceiver; private UsbConnectionBroadcastReceiver mUsbReceiver;
@Mock @Mock
@@ -53,7 +57,8 @@ public class ConnectedUsbDeviceUpdaterTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mDeviceUpdater = new ConnectedUsbDeviceUpdater(mContext, mDevicePreferenceCallback, when(mFragment.getContext()).thenReturn(mContext);
mDeviceUpdater = new ConnectedUsbDeviceUpdater(mFragment, mDevicePreferenceCallback,
mUsbBackend); mUsbBackend);
mDeviceUpdater.mUsbReceiver = mUsbReceiver; mDeviceUpdater.mUsbReceiver = mUsbReceiver;
} }
@@ -70,18 +75,18 @@ public class ConnectedUsbDeviceUpdaterTest {
@Test @Test
public void testInitUsbPreference_usbConnected_preferenceAdded() { public void testInitUsbPreference_usbConnected_preferenceAdded() {
doReturn(true).when(mUsbReceiver).isConnected();
mDeviceUpdater.initUsbPreference(mContext); mDeviceUpdater.initUsbPreference(mContext);
mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(true /* connected */,
UsbBackend.MODE_DATA_NONE);
verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference); verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference);
} }
@Test @Test
public void testInitUsbPreference_usbDisconnected_preferenceRemoved() { public void testInitUsbPreference_usbDisconnected_preferenceRemoved() {
doReturn(false).when(mUsbReceiver).isConnected();
mDeviceUpdater.initUsbPreference(mContext); mDeviceUpdater.initUsbPreference(mContext);
mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(false /* connected */,
UsbBackend.MODE_DATA_NONE);
verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference); verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference);
} }

View File

@@ -14,7 +14,7 @@
* limitations under the License. * 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.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.argThat;
@@ -25,7 +25,9 @@ import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager; 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.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
@@ -46,6 +48,8 @@ public class UsbBackendTest {
private UsbManager mUsbManager; private UsbManager mUsbManager;
@Mock @Mock
private UsbBackend.UserRestrictionUtil mUserRestrictionUtil; private UsbBackend.UserRestrictionUtil mUserRestrictionUtil;
@Mock
private ConnectivityManager mConnectivityManager;
@Before @Before
public void setUp() { public void setUp() {
@@ -53,22 +57,13 @@ public class UsbBackendTest {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI))
.thenReturn(true); .thenReturn(true);
when((Object)mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager); when((Object)mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn((Object) mConnectivityManager);
} }
@Test @Test
public void constructor_noUsbPort_shouldNotCrash() { public void constructor_noUsbPort_shouldNotCrash() {
UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil); UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil, null);
// Should not crash // 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))));
}
} }

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License * 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.assertThat;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
@@ -52,6 +52,8 @@ public class UsbConnectionBroadcastReceiverTest {
@Mock @Mock
private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener; private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener;
@Mock
private UsbBackend mUsbBackend;
@Before @Before
public void setUp() { public void setUp() {
@@ -59,27 +61,42 @@ public class UsbConnectionBroadcastReceiverTest {
mShadowApplication = ShadowApplication.getInstance(); mShadowApplication = ShadowApplication.getInstance();
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener); mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener, mUsbBackend);
} }
@Test @Test
public void testOnReceive_usbConnected_invokeCallback() { public void testOnReceive_usbConnected_invokeCallback() {
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.setAction(UsbManager.ACTION_USB_STATE);
intent.putExtra(UsbManager.USB_CONNECTED, true); intent.putExtra(UsbManager.USB_CONNECTED, true);
mReceiver.onReceive(mContext, intent); mReceiver.onReceive(mContext, intent);
verify(mListener).onUsbConnectionChanged(true); verify(mListener).onUsbConnectionChanged(true /* connected */, UsbBackend.MODE_DATA_NONE);
} }
@Test @Test
public void testOnReceive_usbDisconnected_invokeCallback() { public void testOnReceive_usbDisconnected_invokeCallback() {
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.setAction(UsbManager.ACTION_USB_STATE);
intent.putExtra(UsbManager.USB_CONNECTED, false); intent.putExtra(UsbManager.USB_CONNECTED, false);
mReceiver.onReceive(mContext, intent); 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 @Test

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify;
import android.widget.TextView; import android.widget.TextView;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.connecteddevice.usb.UsbModeChooserActivity;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import org.junit.Before; import org.junit.Before;

View File

@@ -1,16 +1,12 @@
package com.android.settings.connecteddevice; package com.android.settings.connecteddevice.usb;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.deviceinfo.UsbBackend;
import com.android.settings.deviceinfo.UsbModeChooserActivity;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -24,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -33,6 +30,8 @@ public class UsbModePreferenceControllerTest {
private UsbBackend mUsbBackend; private UsbBackend mUsbBackend;
@Mock(answer = RETURNS_DEEP_STUBS) @Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock
private UsbConnectionBroadcastReceiver mUsbConnectionBroadcastReceiver;
private Context mContext; private Context mContext;
private UsbModePreferenceController mController; private UsbModePreferenceController mController;
@@ -42,61 +41,67 @@ public class UsbModePreferenceControllerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = ShadowApplication.getInstance().getApplicationContext(); mContext = ShadowApplication.getInstance().getApplicationContext();
mController = new UsbModePreferenceController(mContext, mUsbBackend); mController = new UsbModePreferenceController(mContext, mUsbBackend);
mController.mUsbReceiver = mUsbConnectionBroadcastReceiver;
} }
@Test @Test
public void testGetSummary_chargeDevice() { public void testGetSummary_chargeDevice() {
assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[0])) assertThat(mController.getSummary(0))
.isEqualTo(R.string.usb_summary_charging_only); .isEqualTo(R.string.usb_summary_charging_only);
} }
@Test @Test
public void testGetSummary_supplyPower() { 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); .isEqualTo(R.string.usb_summary_power_only);
} }
@Test @Test
public void testGetSummary_TransferFiles() { 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); .isEqualTo(R.string.usb_summary_file_transfers);
} }
@Test @Test
public void testGetSummary_TransferPhoto() { 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); .isEqualTo(R.string.usb_summary_photo_transfers);
} }
@Test @Test
public void testGetSummary_MIDI() { public void testGetSummary_MIDI() {
assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[4])) assertThat(mController.getSummary(UsbBackend.MODE_DATA_MIDI))
.isEqualTo(R.string.usb_summary_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 @Test
public void testPreferenceSummary_usbDisconnected() { public void testPreferenceSummary_usbDisconnected() {
final Preference preference = new Preference(mContext); final Preference preference = new Preference(mContext);
preference.setKey("usb_mode"); preference.setKey("usb_mode");
preference.setEnabled(true); preference.setEnabled(true);
when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(false);
mController.updateState(preference); mController.updateState(preference);
assertThat(preference.getKey()).isEqualTo("usb_mode");
assertThat(preference.getSummary()).isEqualTo( assertThat(preference.getSummary()).isEqualTo(
mContext.getString(R.string.disconnected)); mContext.getString(R.string.disconnected));
} }
@Test @Test
public void testUsbBoradcastReceiver_usbConnected_shouldUpdateSummary() { public void testUsbBroadcastReceiver_usbConnected_shouldUpdateSummary() {
final Preference preference = new Preference(mContext); final Preference preference = new Preference(mContext);
preference.setKey("usb_mode"); preference.setKey("usb_mode");
preference.setEnabled(true); preference.setEnabled(true);
when(mUsbBackend.getCurrentMode()).thenReturn(UsbModeChooserActivity.DEFAULT_MODES[0]); when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
when(mScreen.findPreference("usb_mode")).thenReturn(preference); when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(true);
mController.updateState(preference);
mController.displayPreference(mScreen);
mController.onResume();
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.putExtra(UsbManager.USB_CONNECTED, true);
mContext.sendStickyBroadcast(intent);
assertThat(preference.getSummary()).isEqualTo( assertThat(preference.getSummary()).isEqualTo(
mContext.getString(R.string.usb_summary_charging_only)); mContext.getString(R.string.usb_summary_charging_only));

View File

@@ -25,6 +25,7 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@@ -37,11 +38,13 @@ import android.arch.lifecycle.LifecycleOwner;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbManagerExtras;
import android.support.v7.preference.ListPreference; import android.support.v7.preference.ListPreference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.connecteddevice.usb.UsbBackend;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -69,6 +72,8 @@ public class SelectUsbConfigPreferenceControllerTest {
private UsbManager mUsbManager; private UsbManager mUsbManager;
@Mock @Mock
private PackageManager mPackageManager; private PackageManager mPackageManager;
@Mock
private UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
private Context mContext; private Context mContext;
private LifecycleOwner mLifecycleOwner; private LifecycleOwner mLifecycleOwner;
@@ -101,6 +106,13 @@ public class SelectUsbConfigPreferenceControllerTest {
mController = spy(new SelectUsbConfigPreferenceController(mContext, mLifecycle)); mController = spy(new SelectUsbConfigPreferenceController(mContext, mLifecycle));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen); 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 @@ public class SelectUsbConfigPreferenceControllerTest {
@Test @Test
public void onPreferenceChange_setCharging_shouldEnableCharging() { public void onPreferenceChange_setCharging_shouldEnableCharging() {
when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true); when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(
doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean()); UsbManagerExtras.usbFunctionsFromString(mValues[0]));
doNothing().when(mController).setCurrentFunctions(anyLong());
mController.onPreferenceChange(mPreference, mValues[0]); mController.onPreferenceChange(mPreference, mValues[0]);
verify(mController).setCurrentFunction(mValues[0], false /* usb data unlock */); verify(mController).setCurrentFunctions(
UsbManagerExtras.usbFunctionsFromString(mValues[0]));
} }
@Test @Test
@@ -144,28 +158,32 @@ public class SelectUsbConfigPreferenceControllerTest {
@Test @Test
public void onPreferenceChange_setMtp_shouldEnableMtp() { public void onPreferenceChange_setMtp_shouldEnableMtp() {
when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true); when(mUsbManagerPassThrough.getCurrentFunctions())
doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean()); .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
doNothing().when(mController).setCurrentFunctions(anyLong());
mController.onPreferenceChange(mPreference, mValues[1]); mController.onPreferenceChange(mPreference, mValues[1]);
verify(mController).setCurrentFunction(mValues[1], true /* usb data unlock */); verify(mController).setCurrentFunctions(
UsbManagerExtras.usbFunctionsFromString(mValues[1]));
} }
@Test @Test
public void onPreferenceChange_monkeyUser_shouldReturnFalse() { public void onPreferenceChange_monkeyUser_shouldReturnFalse() {
when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true); when(mUsbManagerPassThrough.getCurrentFunctions())
.thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
ShadowUtils.setIsUserAMonkey(true); ShadowUtils.setIsUserAMonkey(true);
doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean()); doNothing().when(mController).setCurrentFunctions(anyLong());
final boolean isHandled = mController.onPreferenceChange(mPreference, mValues[1]); final boolean isHandled = mController.onPreferenceChange(mPreference, mValues[1]);
assertThat(isHandled).isFalse(); assertThat(isHandled).isFalse();
verify(mController, never()).setCurrentFunction(any(), anyBoolean()); verify(mController, never()).setCurrentFunctions(anyLong());
} }
@Test @Test
public void updateState_chargingEnabled_shouldSetPreferenceToCharging() { public void updateState_chargingEnabled_shouldSetPreferenceToCharging() {
when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true); when(mUsbManagerPassThrough.getCurrentFunctions())
.thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[0]));
mController.updateState(mPreference); mController.updateState(mPreference);
@@ -175,7 +193,8 @@ public class SelectUsbConfigPreferenceControllerTest {
@Test @Test
public void updateState_RndisEnabled_shouldEnableRndis() { public void updateState_RndisEnabled_shouldEnableRndis() {
when(mUsbManager.isFunctionEnabled(mValues[3])).thenReturn(true); when(mUsbManagerPassThrough.getCurrentFunctions())
.thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[3]));
mController.updateState(mPreference); mController.updateState(mPreference);
@@ -185,6 +204,7 @@ public class SelectUsbConfigPreferenceControllerTest {
@Test @Test
public void updateState_noValueSet_shouldEnableChargingAsDefault() { public void updateState_noValueSet_shouldEnableChargingAsDefault() {
when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(UsbManagerExtras.NONE);
mController.updateState(mPreference); mController.updateState(mPreference);
verify(mPreference).setValue(mValues[0]); verify(mPreference).setValue(mValues[0]);

View File

@@ -26,6 +26,7 @@ import org.robolectric.annotation.Implements;
public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager { public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray(); private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
private boolean mTetheringSupported = false;
public void setNetworkSupported(int networkType, boolean supported) { public void setNetworkSupported(int networkType, boolean supported) {
mSupportedNetworkTypes.put(networkType, supported); mSupportedNetworkTypes.put(networkType, supported);
@@ -35,4 +36,13 @@ public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowCon
public boolean isNetworkSupported(int networkType) { public boolean isNetworkSupported(int networkType) {
return mSupportedNetworkTypes.get(networkType); return mSupportedNetworkTypes.get(networkType);
} }
public void setTetheringSupported(boolean supported) {
mTetheringSupported = supported;
}
@Implementation
public boolean isTetheringSupported() {
return mTetheringSupported;
}
} }