Commit 7f288af8 authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Add the USB reassign dialog for Crostini and Plugin VM

If a USB device is already assigned to a VM and another requests to
attach it, we show a confirmation dialog.

https://storage.cloud.google.com/chromium-translation-screenshots/e35f1ed0b95833f5b1417ab90fe568efed19a1c2

Bug: 1129266
Change-Id: I08692dcc29db82cb0e15f8e3acc391a86f3684ea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2415913
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarRegan Hsu <hsuregan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808186}
parent 4febae06
......@@ -1214,6 +1214,12 @@
<message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE" desc="Message shown when there are no avaiable USB devices.">
Available USB devices will appear here.
</message>
<message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_IN_USE" desc="Dialog title describing that a USB device is currently in use.">
Device in use
</message>
<message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_REASSIGN" desc="Confirmation explaining to users that reassigning a USB device from being attached from one VM to another could cause errors.">
"<ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph>" is in use. Reassigning the device while it's in use could cause errors. Are you sure you want to continue?
</message>
<message name="IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE" desc="Title of ARC ADB sideloading section.">
Develop Android apps
</message>
......@@ -3105,6 +3111,12 @@
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE" desc="Message shown when there are no avaiable USB devices.">
Available USB devices will appear here.
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_IN_USE" desc="Dialog title describing that a USB device is currently in use.">
Device in use
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_REASSIGN" desc="Confirmation explaining to users that reassigning a USB device from being attached from one VM to another could cause errors.">
"<ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph>" is in use. Reassigning the device while it's in use could cause errors. Are you sure you want to continue?
</message>
<!-- Storage -->
<message name="IDS_SETTINGS_STORAGE_TITLE" desc="In Device Settings, the title for storage management.">
......
e35f1ed0b95833f5b1417ab90fe568efed19a1c2
\ No newline at end of file
e35f1ed0b95833f5b1417ab90fe568efed19a1c2
\ No newline at end of file
e35f1ed0b95833f5b1417ab90fe568efed19a1c2
\ No newline at end of file
e35f1ed0b95833f5b1417ab90fe568efed19a1c2
\ No newline at end of file
......@@ -28,7 +28,8 @@ let CrostiniSharedPath;
/**
* @typedef {{label: string,
* guid: string,
* shared: boolean}}
* shared: boolean,
* shareWillReassign: boolean}}
*/
let CrostiniSharedUsbDevice;
......
......@@ -38,6 +38,27 @@
</div>
</div>
</template>
<template is="dom-if" if="[[reassignDevice_]]" restamp>
<cr-dialog id="reassignDialog" close-text="$i18n{close}"
show-on-attach>
<div slot="title">
$i18n{crostiniSharedUsbDevicesInUse}
</div>
<div slot="body">
[[getReassignDialogText_(reassignDevice_)]]
</div>
<div slot="button-container">
<cr-button id="cancel" class="cancel-button"
on-click="onReassignCancelClick_">
$i18n{cancel}
</cr-button>
<cr-button id="continue" class="action-button"
on-click="onReassignContinueClick_">
$i18n{continue}
</cr-button>
</div>
</cr-dialog>
</template>
</template>
<script src="crostini_shared_usb_devices.js"></script>
</dom-module>
......@@ -11,7 +11,7 @@
Polymer({
is: 'settings-crostini-shared-usb-devices',
behaviors: [WebUIListenerBehavior],
behaviors: [I18nBehavior, WebUIListenerBehavior],
properties: {
/**
......@@ -19,6 +19,16 @@ Polymer({
* @private {Array<!CrostiniSharedUsbDevice>}
*/
sharedUsbDevices_: Array,
/**
* The USB device which was toggled to be shared, but is already shared
* with another VM. When non-null the reassign dialog is shown.
* @private {?CrostiniSharedUsbDevice}
*/
reassignDevice_: {
type: Object,
value: null,
},
},
/** @private {settings.CrostiniBrowserProxy} */
......@@ -46,9 +56,36 @@ Polymer({
* @private
*/
onDeviceSharedChange_(event) {
const deviceInfo = event.model.item;
const device = event.model.item;
// Show reassign dialog if device is already shared with another VM.
if (event.target.checked && device.shareWillReassign) {
event.target.checked = false;
this.reassignDevice_ = device;
return;
}
this.browserProxy_.setCrostiniUsbDeviceShared(
device.guid, event.target.checked);
settings.recordSettingChange();
},
/** @private */
onReassignCancelClick_() {
this.reassignDevice_ = null;
},
/** @private */
onReassignContinueClick_() {
this.browserProxy_.setCrostiniUsbDeviceShared(
deviceInfo.guid, event.target.checked);
this.reassignDevice_.guid, true);
this.reassignDevice_ = null;
settings.recordSettingChange();
},
/**
* @param {!CrostiniSharedUsbDevice} device USB device.
* @private
*/
getReassignDialogText_(device) {
return this.i18n('crostiniSharedUsbDevicesReassign', device.label);
},
});
......@@ -33,6 +33,8 @@ js_library("plugin_vm_shared_paths") {
deps = [
":plugin_vm_browser_proxy",
"../../..:metrics_recorder",
"//ui/webui/resources/js:i18n_behavior",
"//ui/webui/resources/js:web_ui_listener_behavior",
]
}
......
......@@ -21,7 +21,8 @@ let PermissionSetting;
/**
* @typedef {{guid: string,
* label: string,
* shared: boolean}}
* shared: boolean,
* shareWillReassign: boolean}}
*/
let PluginVmSharedUsbDevice;
......
......@@ -40,6 +40,27 @@
</template>
</div>
</template>
<template is="dom-if" if="[[reassignDevice_]]" restamp>
<cr-dialog id="reassignDialog" close-text="$i18n{close}"
show-on-attach>
<div slot="title">
$i18n{pluginVmSharedUsbDevicesInUse}
</div>
<div slot="body">
[[getReassignDialogText_(reassignDevice_)]]
</div>
<div slot="button-container">
<cr-button id="cancel" class="cancel-button"
on-click="onReassignCancelClick_">
$i18n{cancel}
</cr-button>
<cr-button id="continue" class="action-button"
on-click="onReassignContinueClick_">
$i18n{continue}
</cr-button>
</div>
</cr-dialog>
</template>
</template>
<script src="plugin_vm_shared_usb_devices.js"></script>
</dom-module>
......@@ -11,7 +11,7 @@
Polymer({
is: 'settings-plugin-vm-shared-usb-devices',
behaviors: [WebUIListenerBehavior],
behaviors: [I18nBehavior, WebUIListenerBehavior],
properties: {
/**
......@@ -19,6 +19,16 @@ Polymer({
* @private {Array<!PluginVmSharedUsbDevice>}
*/
sharedUsbDevices_: Array,
/**
* The USB device which was toggled to be shared, but is already shared
* with another VM. When non-null the reassign dialog is shown.
* @private {?PluginVmSharedUsbDevice}
*/
reassignDevice_: {
type: Object,
value: null,
},
},
/** @private {settings.PluginVmBrowserProxy} */
......@@ -46,9 +56,36 @@ Polymer({
* @private
*/
onDeviceSharedChange_(event) {
const deviceInfo = event.model.item;
const device = event.model.item;
// Show reassign dialog if device is already shared with another VM.
if (event.target.checked && device.shareWillReassign) {
event.target.checked = false;
this.reassignDevice_ = device;
return;
}
this.browserProxy_.setPluginVmUsbDeviceShared(
device.guid, event.target.checked);
settings.recordSettingChange();
},
/** @private */
onReassignCancelClick_() {
this.reassignDevice_ = null;
},
/** @private */
onReassignContinueClick_() {
this.browserProxy_.setPluginVmUsbDeviceShared(
deviceInfo.guid, event.target.checked);
this.reassignDevice_.guid, true);
this.reassignDevice_ = null;
settings.recordSettingChange();
},
/**
* @param {!PluginVmSharedUsbDevice} device USB device.
* @private
*/
getReassignDialogText_(device) {
return this.i18n('pluginVmSharedUsbDevicesReassign', device.label);
},
});
......@@ -342,6 +342,10 @@ void AppsSection::AddPluginVmLoadTimeData(
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
{"pluginVmSharedUsbDevicesListEmptyMessage",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE},
{"pluginVmSharedUsbDevicesInUse",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_IN_USE},
{"pluginVmSharedUsbDevicesReassign",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_REASSIGN},
{"pluginVmPermissionDialogCameraLabel",
IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL},
{"pluginVmPermissionDialogMicrophoneLabel",
......
......@@ -293,8 +293,10 @@ base::ListValue UsbDevicesToListValue(
base::Value device_info(base::Value::Type::DICTIONARY);
device_info.SetStringKey("guid", device.guid);
device_info.SetStringKey("label", device.label);
device_info.SetBoolKey(
"shared", device.shared_vm_name == crostini::kCrostiniDefaultVmName);
bool shared = device.shared_vm_name == crostini::kCrostiniDefaultVmName;
device_info.SetBoolKey("shared", shared);
device_info.SetBoolKey("shareWillReassign",
device.shared_vm_name && !shared);
usb_devices_list.Append(std::move(device_info));
}
return usb_devices_list;
......
......@@ -270,6 +270,10 @@ void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
{"crostiniSharedUsbDevicesListEmptyMessage",
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE},
{"crostiniSharedUsbDevicesInUse",
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_IN_USE},
{"crostiniSharedUsbDevicesReassign",
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_REASSIGN},
{"crostiniArcAdbTitle", IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE},
{"crostiniArcAdbDescription", IDS_SETTINGS_CROSTINI_ARC_ADB_DESCRIPTION},
{"crostiniArcAdbLabel", IDS_SETTINGS_CROSTINI_ARC_ADB_LABEL},
......
......@@ -27,8 +27,10 @@ base::ListValue UsbDevicesToListValue(
base::Value device_info(base::Value::Type::DICTIONARY);
device_info.SetStringKey("guid", device.guid);
device_info.SetStringKey("label", device.label);
device_info.SetBoolKey("shared",
device.shared_vm_name == plugin_vm::kPluginVmName);
bool shared = device.shared_vm_name == plugin_vm::kPluginVmName;
device_info.SetBoolKey("shared", shared);
device_info.SetBoolKey("shareWillReassign",
device.shared_vm_name && !shared);
usb_devices_list.Append(std::move(device_info));
}
return usb_devices_list;
......
......@@ -35,9 +35,14 @@ suite('SharedUsbDevices', function() {
setup(async function() {
pluginVmBrowserProxy = new TestPluginVmBrowserProxy();
pluginVmBrowserProxy.sharedUsbDevices = [
{guid: '0001', label: 'usb_dev1', shared: true},
{guid: '0002', label: 'usb_dev2', shared: false},
{guid: '0003', label: 'usb_dev3', shared: true},
{
guid: '0001',
label: 'usb_dev1',
shared: false,
shareWillReassign: false,
},
{guid: '0002', label: 'usb_dev2', shared: true, shareWillReassign: false},
{guid: '0003', label: 'usb_dev3', shared: false, shareWillReassign: true},
];
settings.PluginVmBrowserProxyImpl.instance_ = pluginVmBrowserProxy;
PolymerTest.clearBody();
......@@ -64,12 +69,40 @@ suite('SharedUsbDevices', function() {
const args =
await pluginVmBrowserProxy.whenCalled('setPluginVmUsbDeviceShared');
assertEquals('0001', args[0]);
assertEquals(false, args[1]);
assertEquals(true, args[1]);
// Simulate a change in the underlying model.
cr.webUIListenerCallback('plugin-vm-shared-usb-devices-changed', [
{guid: '0001', label: 'usb_dev1', shared: true},
{guid: '0001', label: 'usb_dev1', shared: true, shareWillReassign: false},
]);
Polymer.dom.flush();
assertEquals(1, page.shadowRoot.querySelectorAll('.toggle').length);
});
test('Show dialog for reassign', async function() {
const items = page.shadowRoot.querySelectorAll('.toggle');
assertEquals(3, items.length);
// Clicking on item[2] should show dialog.
assertFalse(!!page.$$('#reassignDialog'));
items[2].click();
Polymer.dom.flush();
assertTrue(page.$$('#reassignDialog').open);
// Clicking cancel will close the dialog.
page.$$('#cancel').click();
Polymer.dom.flush();
assertFalse(!!page.$$('#reassignDialog'));
// Clicking continue will call the proxy and close the dialog.
items[2].click();
Polymer.dom.flush();
assertTrue(page.$$('#reassignDialog').open);
page.$$('#continue').click();
Polymer.dom.flush();
assertFalse(!!page.$$('#reassignDialog'));
const args =
await pluginVmBrowserProxy.whenCalled('setPluginVmUsbDeviceShared');
assertEquals('0003', args[0]);
assertEquals(true, args[1]);
});
});
......@@ -1061,9 +1061,24 @@ suite('CrostiniPageTests', function() {
setup(async function() {
setCrostiniPrefs(true, {
sharedUsbDevices: [
{shared: true, guid: '0001', name: 'usb_dev1'},
{shared: false, guid: '0002', name: 'usb_dev2'},
{shared: true, guid: '0003', name: 'usb_dev3'}
{
guid: '0001',
name: 'usb_dev1',
shared: false,
shareWillReassign: false
},
{
guid: '0002',
name: 'usb_dev2',
shared: true,
shareWillReassign: false
},
{
guid: '0003',
name: 'usb_dev3',
shared: false,
shareWillReassign: true
},
]
});
......@@ -1090,15 +1105,48 @@ suite('CrostiniPageTests', function() {
const args =
await crostiniBrowserProxy.whenCalled('setCrostiniUsbDeviceShared');
assertEquals('0001', args[0]);
assertEquals(false, args[1]);
assertEquals(true, args[1]);
// Simulate a change in the underlying model.
cr.webUIListenerCallback('crostini-shared-usb-devices-changed', [
{shared: true, guid: '0001', name: 'usb_dev1'},
{
guid: '0001',
name: 'usb_dev1',
shared: true,
shareWillReassign: false
},
]);
Polymer.dom.flush();
assertEquals(1, subpage.shadowRoot.querySelectorAll('.toggle').length);
});
test('Show dialog for reassign', async function() {
const items = subpage.shadowRoot.querySelectorAll('.toggle');
assertEquals(3, items.length);
// Clicking on item[2] should show dialog.
assertFalse(!!subpage.$$('#reassignDialog'));
items[2].click();
Polymer.dom.flush();
assertTrue(subpage.$$('#reassignDialog').open);
// Clicking cancel will close the dialog.
subpage.$$('#cancel').click();
Polymer.dom.flush();
assertFalse(!!subpage.$$('#reassignDialog'));
// Clicking continue will call the proxy and close the dialog.
items[2].click();
Polymer.dom.flush();
assertTrue(subpage.$$('#reassignDialog').open);
subpage.$$('#continue').click();
Polymer.dom.flush();
assertFalse(!!subpage.$$('#reassignDialog'));
const args =
await crostiniBrowserProxy.whenCalled('setCrostiniUsbDeviceShared');
assertEquals('0003', args[0]);
assertEquals(true, args[1]);
});
});
suite('SubPageArcAdb', function() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment