Add ARC adb sideload option to Crostini settings
The adb sideloading setting is per device, and requires a reboot to
initiate the flow, such that the user can securely confirm the intention
while the device is in a clean state.
To opt out, the user needs to go through powerwash flow in order to
restore the device back to fully verified state.
Only the device owner can change this device setting, and the setting
applies to all users on the same device. On managed device, the feature
is currently disabled.
entry does not show up
Test: Launch chrome without --enable-features=ArcAdbSideloading, new settings
Test: Launch chrome with --enable-features=ArcAdbSideloading
Test: Only when ARC++ is enabled in pref, the new setting sub menu shows
Test: Go through the enabling flow, and see the value in boot lockbox changed.
Test: Then, go to settings to disable the feature. Powerwash flow is initiated
Test: Non-device owner see the toggle disabled with a policy indicator
Test: On managed device, user see the toggle disabled with a policy indicator
Bug: chromium:893332
Change-Id: I2a709a9ae451d88b4bda87dead45772f63d03f3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1834896
Commit-Queue: Victor Hsieh <victorhsieh@chromium.org>
Auto-Submit: Victor Hsieh <victorhsieh@chromium.org>
Reviewed-by: Tommy Nyquist <nyquist@chromium.org>
Reviewed-by: Alexander Alekseev <alemate@chromium.org>
Reviewed-by: Michael Giuffrida <michaelpg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708328}
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 10f0127..cad48385 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -653,6 +653,30 @@
<message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION" desc="Extra description for managing shared USB devices.">
Only Android devices are currently supported.
</message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE" desc="Title of ARC ADB sideloading section.">
+ Develop Android apps
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_DESCRIPTION" desc="Description of ARC ADB sideloading in Settings.">
+ To create and test your apps, enable the Android Debug Bridge (ADB). Note that this action allows installation of Android apps that haven't been verified by Google, and requires a factory reset to disable.
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_LABEL" desc="Label for enabling ADB in ARC.">
+ Enable on this device
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_ENABLE" desc="Confirmation dialog title to restart for enabling ADB.">
+ Enable ADB?
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_DISABLE" desc="Confirmation dialog title to restart for disabling ADB.">
+ Disable ADB?
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_ENABLE" desc="Describes what will happen if the user enables ADB Sideloading.">
+ To enable the Android Debug Bridge, first restart your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. Disabling ADB requires a reset to factory settings.
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_DISABLE" desc="Describes what will happen if the user disables ADB Sideloading.">
+ To disable ADB, restart your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. It will be reset to factory settings, and all user accounts and local data will be erased.
+ </message>
+ <message name="IDS_SETTINGS_CROSTINI_ARC_ADB_RESTART_BUTTON" desc="Label for the button that initiates the ADB enabling flow by restarting the device.">
+ Restart
+ </message>
<!-- Plugin VM Page -->
<message name="IDS_SETTINGS_PLUGIN_VM_PAGE_TITLE" desc="The title of Plugin VM section.">
diff --git a/chrome/browser/resources/settings/crostini_page/BUILD.gn b/chrome/browser/resources/settings/crostini_page/BUILD.gn
index 2e4035e..1bd08b223 100644
--- a/chrome/browser/resources/settings/crostini_page/BUILD.gn
+++ b/chrome/browser/resources/settings/crostini_page/BUILD.gn
@@ -6,6 +6,7 @@
js_type_check("closure_compile") {
deps = [
+ ":crostini_arc_adb",
":crostini_browser_proxy",
":crostini_export_import",
":crostini_page",
@@ -15,6 +16,15 @@
]
}
+js_library("crostini_arc_adb") {
+ deps = [
+ ":crostini_browser_proxy",
+ "..:route",
+ "//ui/webui/resources/cr_elements/policy:cr_policy_indicator",
+ "//ui/webui/resources/js:web_ui_listener_behavior",
+ ]
+}
+
js_library("crostini_browser_proxy") {
deps = [
"//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.html b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.html
new file mode 100644
index 0000000..3b4cd5b
--- /dev/null
+++ b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.html
@@ -0,0 +1,39 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="crostini_browser_proxy.html">
+<link rel="import" href="crostini_arc_adb_confirmation_dialog.html">
+<link rel="import" href="../i18n_setup.html">
+<link rel="import" href="../settings_shared_css.html">
+
+<dom-module id="settings-crostini-arc-adb">
+ <template>
+ <style include="settings-shared"></style>
+ <div class="settings-box first">
+ <span>$i18n{crostiniArcAdbDescription}</span>
+ </div>
+ <div class="settings-box">
+ <div id="enableArcAdbLabel" class="start">
+ $i18n{crostiniArcAdbLabel}
+ </div>
+ <cr-policy-indicator indicator-type="[[getPolicyIndicatorType_(
+ isOwnerProfile_, isEnterpriseManaged_)]]"></cr-policy-indicator>
+ <cr-toggle id="arcAdbEnabledButton" aria-labelledby="enableArcAdbLabel"
+ checked$="[[arcAdbEnabled_]]"
+ disabled="[[shouldDisable_(isOwnerProfile_, isEnterpriseManaged_)]]"
+ on-change="onArcAdbToggleChanged_">
+ </cr-toggle>
+ </div>
+
+ <template is="dom-if" if="[[showConfirmationDialog_]]" restamp>
+ <settings-crostini-arc-adb-confirmation-dialog
+ action="[[getToggleAction_(arcAdbEnabled_)]]"
+ on-close="onConfirmationDialogClose_">
+ </settings-crostini-arc-adb-confirmation-dialog>
+ </template>
+ </template>
+ <script src="crostini_arc_adb.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.js b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.js
new file mode 100644
index 0000000..3665e26
--- /dev/null
+++ b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb.js
@@ -0,0 +1,91 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'crostini-arc-adb' is the ARC adb sideloading subpage for Crostini.
+ */
+
+Polymer({
+ is: 'settings-crostini-arc-adb',
+
+ behaviors: [WebUIListenerBehavior],
+
+ properties: {
+ /** @private {boolean} */
+ arcAdbEnabled_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @private {boolean} */
+ isOwnerProfile_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('isOwnerProfile');
+ },
+ },
+
+ /** @private {boolean} */
+ isEnterpriseManaged_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('isEnterpriseManaged');
+ },
+ },
+
+ /** @private {boolean} */
+ showConfirmationDialog_: {
+ type: Boolean,
+ value: false,
+ },
+ },
+
+ attached: function() {
+ this.addWebUIListener(
+ 'crostini-arc-adb-sideload-status-changed', (enabled) => {
+ this.arcAdbEnabled_ = enabled;
+ });
+ settings.CrostiniBrowserProxyImpl.getInstance()
+ .requestArcAdbSideloadStatus();
+ },
+
+ /**
+ * Returns whether the toggle is changeable to the user. Only the device owner
+ * is able to change it. Note that the actual guard should be in browser,
+ * otherwise a user may bypass this check by inspecting Settings with
+ * developer tool.
+ * @private
+ */
+ shouldDisable_: function(isOwnerProfile, isEnterpriseManaged) {
+ return !isOwnerProfile || isEnterpriseManaged;
+ },
+
+ /** @private */
+ getPolicyIndicatorType_: function(isOwnerProfile, isEnterpriseManaged) {
+ if (isEnterpriseManaged) {
+ return CrPolicyIndicatorType.DEVICE_POLICY;
+ } else if (!isOwnerProfile) {
+ return CrPolicyIndicatorType.OWNER;
+ } else {
+ return CrPolicyIndicatorType.NONE;
+ }
+ },
+
+ /** @private */
+ getToggleAction_: function(arcAdbEnabled) {
+ return arcAdbEnabled ? 'disable' : 'enable';
+ },
+
+ /** @private */
+ onArcAdbToggleChanged_: function(event) {
+ this.showConfirmationDialog_ = true;
+ },
+
+ /** @private */
+ onConfirmationDialogClose_: function() {
+ this.showConfirmationDialog_ = false;
+ this.$.arcAdbEnabledButton.checked = this.arcAdbEnabled_;
+ },
+});
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.html b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.html
new file mode 100644
index 0000000..664f85c
--- /dev/null
+++ b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.html
@@ -0,0 +1,36 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="crostini_browser_proxy.html">
+<link rel="import" href="../settings_shared_css.html">
+
+<dom-module id="settings-crostini-arc-adb-confirmation-dialog">
+ <template>
+ <style include="settings-shared"></style>
+ <cr-dialog id="dialog" close-text="$i18n{close}">
+ <div slot="title" hidden="[[!isEnabling_(action)]]">
+ $i18n{crostiniArcAdbConfirmationTitleEnable}
+ </div>
+ <div slot="title" hidden="[[!isDisabling_(action)]]">
+ $i18n{crostiniArcAdbConfirmationTitleDisable}
+ </div>
+ <div slot="body" hidden="[[!isEnabling_(action)]]">
+ $i18n{crostiniArcAdbConfirmationMessageEnable}
+ </div>
+ <div slot="body" hidden="[[!isDisabling_(action)]]">
+ $i18n{crostiniArcAdbConfirmationMessageDisable}
+ </div>
+ <div slot="button-container">
+ <cr-button id="cancel" class="cancel-button" on-click="onCancelTap_">
+ $i18n{cancel}
+ </cr-button>
+ <cr-button id="continue" class="action-button" on-click="onRestartTap_">
+ $i18n{crostiniArcAdbRestartButton}
+ </cr-button>
+ </div>
+ </cr-dialog>
+ </template>
+ <script src="crostini_arc_adb_confirmation_dialog.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.js b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.js
new file mode 100644
index 0000000..6b64eb55
--- /dev/null
+++ b/chrome/browser/resources/settings/crostini_page/crostini_arc_adb_confirmation_dialog.js
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview 'settings-crostini-arc-adb-confirmation-dialog' is a component
+ * to confirm for enabling or disabling adb sideloading. After the confirmation,
+ * reboot will happens.
+ */
+Polymer({
+ is: 'settings-crostini-arc-adb-confirmation-dialog',
+
+ properties: {
+ /** An attribute that indicates the action for the confirmation */
+ action: {
+ type: String,
+ },
+ },
+
+ /** @override */
+ attached: function() {
+ this.$.dialog.showModal();
+ },
+
+ /** @override */
+ isEnabling_: function() {
+ return this.action === 'enable';
+ },
+
+ /** @override */
+ isDisabling_: function() {
+ return this.action === 'disable';
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ this.$.dialog.close();
+ },
+
+ /** @private */
+ onRestartTap_: function() {
+ if (this.isEnabling_()) {
+ settings.CrostiniBrowserProxyImpl.getInstance().enableArcAdbSideload();
+ } else if (this.isDisabling_()) {
+ settings.CrostiniBrowserProxyImpl.getInstance().disableArcAdbSideload();
+ } else {
+ assertNotReached();
+ }
+ },
+});
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
index f9b965ea..3bd9508e 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/crostini_page/crostini_browser_proxy.js
@@ -72,6 +72,15 @@
* Import crostini container.
*/
importCrostiniContainer() {}
+
+ /** Queries the current status of ARC ADB Sideloading. */
+ requestArcAdbSideloadStatus() {}
+
+ /** Initiates the flow to enable ARC ADB Sideloading. */
+ enableArcAdbSideload() {}
+
+ /** Initiates the flow to disable ARC ADB Sideloading. */
+ disableArcAdbSideload() {}
}
/** @implements {settings.CrostiniBrowserProxy} */
@@ -125,6 +134,21 @@
importCrostiniContainer() {
chrome.send('importCrostiniContainer');
}
+
+ /** @override */
+ requestArcAdbSideloadStatus() {
+ chrome.send('requestArcAdbSideloadStatus');
+ }
+
+ /** @override */
+ enableArcAdbSideload() {
+ chrome.send('enableArcAdbSideload');
+ }
+
+ /** @override */
+ disableArcAdbSideload() {
+ chrome.send('disableArcAdbSideload');
+ }
}
// The singleton instance_ can be replaced with a test version of this wrapper
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_page.html b/chrome/browser/resources/settings/crostini_page/crostini_page.html
index d241d5ba..85004114 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_page.html
+++ b/chrome/browser/resources/settings/crostini_page/crostini_page.html
@@ -9,6 +9,7 @@
<link rel="import" href="../settings_page/settings_animated_pages.html">
<link rel="import" href="../settings_page/settings_subpage.html">
<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="crostini_arc_adb.html">
<link rel="import" href="crostini_browser_proxy.html">
<link rel="import" href="crostini_export_import.html">
<link rel="import" href="crostini_shared_paths.html">
@@ -62,6 +63,15 @@
</settings-subpage>
</template>
+ <template is="dom-if" route-path="/crostini/androidAdb">
+ <settings-subpage
+ associated-control="[[$$('#crostini')]]"
+ page-title="$i18n{crostiniArcAdbTitle}">
+ <settings-crostini-arc-adb prefs="{{prefs}}">
+ </settings-crostini-arc-adb>
+ </settings-subpage>
+ </template>
+
<template is="dom-if" route-path="/crostini/exportImport">
<settings-subpage
associated-control="[[$$('#crostini')]]"
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_subpage.html b/chrome/browser/resources/settings/crostini_page/crostini_subpage.html
index c77f616..5465713 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_subpage.html
+++ b/chrome/browser/resources/settings/crostini_page/crostini_subpage.html
@@ -31,6 +31,14 @@
on-click="onExportImportClick_">
</cr-link-row>
</template>
+ <template is="dom-if" if="[[showArcAdbSideloading_]]">
+ <cr-link-row
+ class="hr"
+ label="$i18n{crostiniArcAdbTitle}"
+ id="crostini-enable-arc-adb"
+ on-click="onEnableArcAdbClick_">
+ </cr-link-row>
+ </template>
<template is="dom-if" if="[[!hideCrostiniUninstall_]]">
<div id="remove" class="settings-box">
<div id="removeCrostiniLabel" class="start">$i18n{crostiniRemove}</div>
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_subpage.js b/chrome/browser/resources/settings/crostini_page/crostini_subpage.js
index 66d8ca13f..16a18df7 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_subpage.js
+++ b/chrome/browser/resources/settings/crostini_page/crostini_subpage.js
@@ -30,6 +30,25 @@
},
},
+ /** @private {boolean} */
+ showArcAdbSideloading_: {
+ type: Boolean,
+ computed: 'and_(isArcAdbSideloadingSupported_, isAndroidEnabled_)',
+ },
+
+ /** @private {boolean} */
+ isArcAdbSideloadingSupported_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('ArcAdbSideloadingSupported');
+ },
+ },
+
+ /** @private {boolean} */
+ isAndroidEnabled_: {
+ type: Boolean,
+ },
+
/**
* Whether the uninstall options should be displayed.
* @private {boolean}
@@ -39,7 +58,10 @@
},
},
- observers: ['onCrostiniEnabledChanged_(prefs.crostini.enabled.value)'],
+ observers: [
+ 'onCrostiniEnabledChanged_(prefs.crostini.enabled.value)',
+ 'onArcEnabledChanged_(prefs.arc.enabled.value)'
+ ],
attached: function() {
const callback = (status) => {
@@ -59,10 +81,20 @@
},
/** @private */
+ onArcEnabledChanged_: function(enabled) {
+ this.isAndroidEnabled_ = enabled;
+ },
+
+ /** @private */
onExportImportClick_: function() {
settings.navigateTo(settings.routes.CROSTINI_EXPORT_IMPORT);
},
+ /** @private */
+ onEnableArcAdbClick_: function() {
+ settings.navigateTo(settings.routes.CROSTINI_ANDROID_ADB);
+ },
+
/**
* Shows a confirmation dialog when removing crostini.
* @private
@@ -80,4 +112,9 @@
onSharedUsbDevicesClick_: function() {
settings.navigateTo(settings.routes.CROSTINI_SHARED_USB_DEVICES);
},
+
+ /** @private */
+ and_: function(a, b) {
+ return a && b;
+ },
});
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 9d683c7..776139a 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -1147,6 +1147,18 @@
<structure name="IDR_OS_SETTINGS_CROSTINI_SUBPAGE_JS"
file="crostini_page/crostini_subpage.js"
type="chrome_html" />
+ <structure name="IDR_OS_SETTINGS_CROSTINI_ARC_ADB_HTML"
+ file="crostini_page/crostini_arc_adb.html"
+ type="chrome_html" />
+ <structure name="IDR_OS_SETTINGS_CROSTINI_ARC_ADB_JS"
+ file="crostini_page/crostini_arc_adb.js"
+ type="chrome_html" />
+ <structure name="IDR_OS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_DIALOG_HTML"
+ file="crostini_page/crostini_arc_adb_confirmation_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_OS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_DIALOG_JS"
+ file="crostini_page/crostini_arc_adb_confirmation_dialog.js"
+ type="chrome_html" />
<structure name="IDR_OS_SETTINGS_CROSTINI_EXPORT_IMPORT_HTML"
file="crostini_page/crostini_export_import.html"
type="chrome_html" />
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 4084819..9c6b8150 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -19,6 +19,7 @@
* ANDROID_APPS: (undefined|!settings.Route),
* ANDROID_APPS_DETAILS: (undefined|!settings.Route),
* CROSTINI: (undefined|!settings.Route),
+ * CROSTINI_ANDROID_ADB: (undefined|!settings.Route),
* CROSTINI_DETAILS: (undefined|!settings.Route),
* CROSTINI_EXPORT_IMPORT: (undefined|!settings.Route),
* CROSTINI_SHARED_PATHS: (undefined|!settings.Route),
@@ -471,6 +472,7 @@
if (loadTimeData.valueExists('showCrostini') &&
loadTimeData.getBoolean('showCrostini')) {
r.CROSTINI = r.BASIC.createSection('/crostini', 'crostini');
+ r.CROSTINI_ANDROID_ADB = r.CROSTINI.createChild('/crostini/androidAdb');
r.CROSTINI_DETAILS = r.CROSTINI.createChild('/crostini/details');
if (loadTimeData.valueExists('showCrostiniExportImport') &&
loadTimeData.getBoolean('showCrostiniExportImport')) {
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index ea0b1a10..42e999f 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1362,6 +1362,18 @@
<structure name="IDR_SETTINGS_CROSTINI_SUBPAGE_JS"
file="crostini_page/crostini_subpage.js"
type="chrome_html" />
+ <structure name="IDR_SETTINGS_CROSTINI_ARC_ADB_HTML"
+ file="crostini_page/crostini_arc_adb.html"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_CROSTINI_ARC_ADB_JS"
+ file="crostini_page/crostini_arc_adb.js"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_DIALOG_HTML"
+ file="crostini_page/crostini_arc_adb_confirmation_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_DIALOG_JS"
+ file="crostini_page/crostini_arc_adb_confirmation_dialog.js"
+ type="chrome_html" />
<structure name="IDR_SETTINGS_CROSTINI_EXPORT_IMPORT_HTML"
file="crostini_page/crostini_export_import.html"
type="chrome_html" />
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
index 2a4d10f..88e4f94 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -9,10 +9,19 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/file_manager/path_util.h"
#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chromeos/dbus/session_manager/session_manager_client.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
namespace chromeos {
@@ -68,6 +77,18 @@
base::BindRepeating(
&CrostiniHandler::HandleCrostiniExportImportOperationStatusRequest,
weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "requestArcAdbSideloadStatus",
+ base::BindRepeating(&CrostiniHandler::HandleQueryArcAdbRequest,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "enableArcAdbSideload",
+ base::BindRepeating(&CrostiniHandler::HandleEnableArcAdbRequest,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "disableArcAdbSideload",
+ base::BindRepeating(&CrostiniHandler::HandleDisableArcAdbRequest,
+ weak_ptr_factory_.GetWeakPtr()));
}
void CrostiniHandler::OnJavascriptAllowed() {
@@ -254,5 +275,70 @@
base::Value(in_progress));
}
+void CrostiniHandler::HandleQueryArcAdbRequest(const base::ListValue* args) {
+ AllowJavascript();
+ CHECK_EQ(0U, args->GetSize());
+
+ chromeos::SessionManagerClient* client =
+ chromeos::SessionManagerClient::Get();
+ client->QueryAdbSideload(base::Bind(&CrostiniHandler::OnQueryAdbSideload,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrostiniHandler::OnQueryAdbSideload(bool success, bool enabled) {
+ if (!success) {
+ LOG(ERROR) << "Failed to query adb sideload status";
+ enabled = false;
+ }
+ // Other side listens with cr.addWebUIListener
+ FireWebUIListener("crostini-arc-adb-sideload-status-changed",
+ base::Value(enabled));
+}
+
+void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) {
+ CHECK_EQ(0U, args->GetSize());
+ if (!CheckEligibilityToChangeArcAdbSideloading())
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kEnableAdbSideloadingRequested, true);
+ prefs->CommitPendingWrite();
+
+ chrome::AttemptRelaunch();
+}
+
+void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) {
+ CHECK_EQ(0U, args->GetSize());
+ if (!CheckEligibilityToChangeArcAdbSideloading())
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kFactoryResetRequested, true);
+ prefs->CommitPendingWrite();
+
+ chromeos::PowerManagerClient::Get()->RequestRestart(
+ power_manager::REQUEST_RESTART_FOR_USER, "disable adb sideloading");
+}
+
+bool CrostiniHandler::CheckEligibilityToChangeArcAdbSideloading() const {
+ if (!chromeos::ProfileHelper::IsOwnerProfile(profile_)) {
+ DVLOG(1) << "Only the owner can change adb sideloading status";
+ return false;
+ }
+
+ if (user_manager::UserManager::Get()->IsLoggedInAsChildUser()) {
+ DVLOG(1) << "Child account is currently unsupported";
+ return false;
+ }
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->IsEnterpriseManaged()) {
+ DVLOG(1) << "adb sideloading is currently unsupported on managed device";
+ return false;
+ }
+ return true;
+}
+
} // namespace settings
} // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
index c5d4a353..1141963 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
@@ -63,6 +63,17 @@
const base::ListValue* args);
// CrostiniExportImport::Observer:
void OnCrostiniExportImportOperationStatusChanged(bool in_progress) override;
+ // Handle a request for querying status of ARC adb sideloading.
+ void HandleQueryArcAdbRequest(const base::ListValue* args);
+ // Handle a request for enabling adb sideloading in ARC.
+ void HandleEnableArcAdbRequest(const base::ListValue* args);
+ // Handle a request for disabling adb sideloading in ARC.
+ void HandleDisableArcAdbRequest(const base::ListValue* args);
+ // Callback of HandleQueryArcAdbRequest.
+ void OnQueryAdbSideload(bool success, bool enabled);
+ // Returns whether the current user can change adb sideloading configuration
+ // on current device.
+ bool CheckEligibilityToChangeArcAdbSideloading() const;
Profile* profile_;
// weak_ptr_factory_ should always be last member.
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 465410f..4bcb7f14 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -19,6 +19,7 @@
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_shortcut_manager.h"
@@ -137,6 +138,12 @@
return base::ASCIIToUTF16(original_url +
"&b=" + base::SysInfo::GetLsbReleaseBoard());
}
+
+bool IsEnterpriseManaged() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->IsEnterpriseManaged();
+}
#endif
void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) {
@@ -566,6 +573,15 @@
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
{"crostiniSharedUsbDevicesListEmptyMessage",
IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE},
+ {"crostiniArcAdbTitle", IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE},
+ {"crostiniArcAdbDescription", IDS_SETTINGS_CROSTINI_ARC_ADB_DESCRIPTION},
+ {"crostiniArcAdbLabel", IDS_SETTINGS_CROSTINI_ARC_ADB_LABEL},
+ {"crostiniArcAdbRestartButton",
+ IDS_SETTINGS_CROSTINI_ARC_ADB_RESTART_BUTTON},
+ {"crostiniArcAdbConfirmationTitleEnable",
+ IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_ENABLE},
+ {"crostiniArcAdbConfirmationTitleDisable",
+ IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_DISABLE},
};
AddLocalizedStringsBulk(html_source, kLocalizedStrings,
base::size(kLocalizedStrings));
@@ -578,6 +594,16 @@
IDS_SETTINGS_CROSTINI_REMOVE,
ui::GetChromeOSDeviceName()));
html_source->AddString(
+ "crostiniArcAdbConfirmationMessageEnable",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_ENABLE,
+ ui::GetChromeOSDeviceName()));
+ html_source->AddString(
+ "crostiniArcAdbConfirmationMessageDisable",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_DISABLE,
+ ui::GetChromeOSDeviceName()));
+ html_source->AddString(
"crostiniSharedPathsInstructionsLocate",
l10n_util::GetStringFUTF16(
IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE,
@@ -586,6 +612,12 @@
html_source->AddBoolean(
"showCrostiniExportImport",
crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile));
+ html_source->AddBoolean("ArcAdbSideloadingSupported",
+ base::FeatureList::IsEnabled(
+ chromeos::features::kArcAdbSideloadingFeature));
+ html_source->AddBoolean("isOwnerProfile",
+ chromeos::ProfileHelper::IsOwnerProfile(profile));
+ html_source->AddBoolean("isEnterpriseManaged", IsEnterpriseManaged());
}
void AddPluginVmStrings(content::WebUIDataSource* html_source,
@@ -1670,10 +1702,7 @@
html_source->AddBoolean("isActiveDirectoryUser",
user && user->IsActiveDirectoryUser());
- policy::BrowserPolicyConnectorChromeOS* connector =
- g_browser_process->platform_part()->browser_policy_connector_chromeos();
- if (!connector->IsEnterpriseManaged() &&
- !user_manager->IsCurrentUserOwner()) {
+ if (!IsEnterpriseManaged() && !user_manager->IsCurrentUserOwner()) {
html_source->AddString("ownerEmail",
user_manager->GetOwnerAccountId().GetUserEmail());
}
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 13ade0c..4cad314 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2186,6 +2186,9 @@
// Indicates that debugging features were requested from oobe screen.
const char kDebuggingFeaturesRequested[] = "DebuggingFeaturesRequested";
+// Indicates that the user has requested that ARC APK Sideloading be enabled.
+const char kEnableAdbSideloadingRequested[] = "EnableAdbSideloadingRequested";
+
#if defined(OS_CHROMEOS)
// This setting controls initial device timezone that is used before user
// session started. It is controlled by device owner.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index a2801a5..e9db519 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -768,6 +768,7 @@
extern const char kFactoryResetRequested[];
extern const char kFactoryResetTPMFirmwareUpdateMode[];
extern const char kDebuggingFeaturesRequested[];
+extern const char kEnableAdbSideloadingRequested[];
#if defined(OS_CHROMEOS)
extern const char kSigninScreenTimezone[];
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 7dbeee4..f938da76 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -20,6 +20,10 @@
const base::Feature kAmbientModeFeature{"ChromeOSAmbientMode",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls whether to enable ARC ADB sideloading support.
+const base::Feature kArcAdbSideloadingFeature{
+ "ArcAdbSideloading", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enables or disables auto screen-brightness adjustment when ambient light
// changes.
const base::Feature kAutoScreenBrightness{"AutoScreenBrightness",
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 5e50e5b..732b2e8 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -18,6 +18,8 @@
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kAmbientModeFeature;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kArcAdbSideloadingFeature;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kAutoScreenBrightness;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kBluetoothAggressiveAppearanceFilter;