1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/settings/chromeos/crostini_handler.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback_forward.h"
12 #include "base/callback_helpers.h"
13 #include "base/metrics/histogram_functions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/crostini/crostini_disk.h"
16 #include "chrome/browser/chromeos/crostini/crostini_features.h"
17 #include "chrome/browser/chromeos/crostini/crostini_installer.h"
18 #include "chrome/browser/chromeos/crostini/crostini_port_forwarder.h"
19 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
20 #include "chrome/browser/chromeos/crostini/crostini_types.mojom.h"
21 #include "chrome/browser/chromeos/crostini/crostini_util.h"
22 #include "chrome/browser/chromeos/file_manager/path_util.h"
23 #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
24 #include "chrome/browser/chromeos/profiles/profile_helper.h"
25 #include "chrome/browser/lifetime/application_lifetime.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/prefs/pref_service.h"
30 #include "components/user_manager/user_manager.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "ui/display/screen.h"
33
34 namespace chromeos {
35 namespace settings {
36
37 namespace {
38
39 // These values are used for metrics and should not change.
40 enum class CrostiniSettingsEvent {
41 kEnableAdbSideloading = 0,
42 kDisableAdbSideloading = 1,
43 kMaxValue = kDisableAdbSideloading,
44 };
45
LogEvent(CrostiniSettingsEvent action)46 void LogEvent(CrostiniSettingsEvent action) {
47 base::UmaHistogramEnumeration("Crostini.SettingsEvent", action);
48 }
49
50 } // namespace
51
CrostiniHandler(Profile * profile)52 CrostiniHandler::CrostiniHandler(Profile* profile) : profile_(profile) {}
53
~CrostiniHandler()54 CrostiniHandler::~CrostiniHandler() {
55 DisallowJavascript();
56 }
57
RegisterMessages()58 void CrostiniHandler::RegisterMessages() {
59 web_ui()->RegisterMessageCallback(
60 "requestCrostiniInstallerView",
61 base::BindRepeating(&CrostiniHandler::HandleRequestCrostiniInstallerView,
62 weak_ptr_factory_.GetWeakPtr()));
63 web_ui()->RegisterMessageCallback(
64 "requestRemoveCrostini",
65 base::BindRepeating(&CrostiniHandler::HandleRequestRemoveCrostini,
66 weak_ptr_factory_.GetWeakPtr()));
67 web_ui()->RegisterMessageCallback(
68 "getCrostiniSharedPathsDisplayText",
69 base::BindRepeating(
70 &CrostiniHandler::HandleGetCrostiniSharedPathsDisplayText,
71 weak_ptr_factory_.GetWeakPtr()));
72 web_ui()->RegisterMessageCallback(
73 "removeCrostiniSharedPath",
74 base::BindRepeating(&CrostiniHandler::HandleRemoveCrostiniSharedPath,
75 weak_ptr_factory_.GetWeakPtr()));
76 web_ui()->RegisterMessageCallback(
77 "notifyCrostiniSharedUsbDevicesPageReady",
78 base::BindRepeating(
79 &CrostiniHandler::HandleNotifyCrostiniSharedUsbDevicesPageReady,
80 weak_ptr_factory_.GetWeakPtr()));
81 web_ui()->RegisterMessageCallback(
82 "setCrostiniUsbDeviceShared",
83 base::BindRepeating(&CrostiniHandler::HandleSetCrostiniUsbDeviceShared,
84 weak_ptr_factory_.GetWeakPtr()));
85 web_ui()->RegisterMessageCallback(
86 "exportCrostiniContainer",
87 base::BindRepeating(&CrostiniHandler::HandleExportCrostiniContainer,
88 weak_ptr_factory_.GetWeakPtr()));
89 web_ui()->RegisterMessageCallback(
90 "importCrostiniContainer",
91 base::BindRepeating(&CrostiniHandler::HandleImportCrostiniContainer,
92 weak_ptr_factory_.GetWeakPtr()));
93 web_ui()->RegisterMessageCallback(
94 "requestCrostiniInstallerStatus",
95 base::BindRepeating(
96 &CrostiniHandler::HandleCrostiniInstallerStatusRequest,
97 weak_ptr_factory_.GetWeakPtr()));
98 web_ui()->RegisterMessageCallback(
99 "requestCrostiniExportImportOperationStatus",
100 base::BindRepeating(
101 &CrostiniHandler::HandleCrostiniExportImportOperationStatusRequest,
102 weak_ptr_factory_.GetWeakPtr()));
103 web_ui()->RegisterMessageCallback(
104 "requestArcAdbSideloadStatus",
105 base::BindRepeating(&CrostiniHandler::HandleQueryArcAdbRequest,
106 weak_ptr_factory_.GetWeakPtr()));
107 web_ui()->RegisterMessageCallback(
108 "getCanChangeArcAdbSideloading",
109 base::BindRepeating(
110 &CrostiniHandler::HandleCanChangeArcAdbSideloadingRequest,
111 weak_ptr_factory_.GetWeakPtr()));
112 web_ui()->RegisterMessageCallback(
113 "enableArcAdbSideload",
114 base::BindRepeating(&CrostiniHandler::HandleEnableArcAdbRequest,
115 weak_ptr_factory_.GetWeakPtr()));
116 web_ui()->RegisterMessageCallback(
117 "disableArcAdbSideload",
118 base::BindRepeating(&CrostiniHandler::HandleDisableArcAdbRequest,
119 weak_ptr_factory_.GetWeakPtr()));
120 web_ui()->RegisterMessageCallback(
121 "requestCrostiniContainerUpgradeView",
122 base::BindRepeating(&CrostiniHandler::HandleRequestContainerUpgradeView,
123 weak_ptr_factory_.GetWeakPtr()));
124 web_ui()->RegisterMessageCallback(
125 "requestCrostiniUpgraderDialogStatus",
126 base::BindRepeating(
127 &CrostiniHandler::HandleCrostiniUpgraderDialogStatusRequest,
128 weak_ptr_factory_.GetWeakPtr()));
129 web_ui()->RegisterMessageCallback(
130 "requestCrostiniContainerUpgradeAvailable",
131 base::BindRepeating(
132 &CrostiniHandler::HandleCrostiniContainerUpgradeAvailableRequest,
133 weak_ptr_factory_.GetWeakPtr()));
134 web_ui()->RegisterMessageCallback(
135 "addCrostiniPortForward",
136 base::BindRepeating(&CrostiniHandler::HandleAddCrostiniPortForward,
137 weak_ptr_factory_.GetWeakPtr()));
138 web_ui()->RegisterMessageCallback(
139 "getCrostiniDiskInfo",
140 base::BindRepeating(&CrostiniHandler::HandleGetCrostiniDiskInfo,
141 weak_ptr_factory_.GetWeakPtr()));
142 web_ui()->RegisterMessageCallback(
143 "resizeCrostiniDisk",
144 base::BindRepeating(&CrostiniHandler::HandleResizeCrostiniDisk,
145 weak_ptr_factory_.GetWeakPtr()));
146 web_ui()->RegisterMessageCallback(
147 "checkCrostiniMicSharingStatus",
148 base::BindRepeating(&CrostiniHandler::HandleCheckCrostiniMicSharingStatus,
149 weak_ptr_factory_.GetWeakPtr()));
150 web_ui()->RegisterMessageCallback(
151 "removeCrostiniPortForward",
152 base::BindRepeating(&CrostiniHandler::HandleRemoveCrostiniPortForward,
153 weak_ptr_factory_.GetWeakPtr()));
154 web_ui()->RegisterMessageCallback(
155 "removeAllCrostiniPortForwards",
156 base::BindRepeating(&CrostiniHandler::HandleRemoveAllCrostiniPortForwards,
157 weak_ptr_factory_.GetWeakPtr()));
158 web_ui()->RegisterMessageCallback(
159 "activateCrostiniPortForward",
160 base::BindRepeating(&CrostiniHandler::HandleActivateCrostiniPortForward,
161 weak_ptr_factory_.GetWeakPtr()));
162 web_ui()->RegisterMessageCallback(
163 "deactivateCrostiniPortForward",
164 base::BindRepeating(&CrostiniHandler::HandleDeactivateCrostiniPortForward,
165 weak_ptr_factory_.GetWeakPtr()));
166 web_ui()->RegisterMessageCallback(
167 "getCrostiniActivePorts",
168 base::BindRepeating(&CrostiniHandler::HandleGetCrostiniActivePorts,
169 weak_ptr_factory_.GetWeakPtr()));
170 web_ui()->RegisterMessageCallback(
171 "checkCrostiniIsRunning",
172 base::BindRepeating(&CrostiniHandler::HandleCheckCrostiniIsRunning,
173 weak_ptr_factory_.GetWeakPtr()));
174 web_ui()->RegisterMessageCallback(
175 "shutdownCrostini",
176 base::BindRepeating(&CrostiniHandler::HandleShutdownCrostini,
177 weak_ptr_factory_.GetWeakPtr()));
178 web_ui()->RegisterMessageCallback(
179 "setCrostiniMicSharingEnabled",
180 base::BindRepeating(&CrostiniHandler::HandleSetCrostiniMicSharingEnabled,
181 weak_ptr_factory_.GetWeakPtr()));
182 web_ui()->RegisterMessageCallback(
183 "getCrostiniMicSharingEnabled",
184 base::BindRepeating(&CrostiniHandler::HandleGetCrostiniMicSharingEnabled,
185 weak_ptr_factory_.GetWeakPtr()));
186 }
187
OnJavascriptAllowed()188 void CrostiniHandler::OnJavascriptAllowed() {
189 auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_);
190 crostini_manager->AddCrostiniDialogStatusObserver(this);
191 crostini_manager->AddCrostiniContainerPropertiesObserver(this);
192 crostini_manager->AddContainerStartedObserver(this);
193 crostini_manager->AddContainerShutdownObserver(this);
194 crostini_manager->AddCrostiniMicSharingEnabledObserver(this);
195 if (chromeos::CrosUsbDetector::Get()) {
196 chromeos::CrosUsbDetector::Get()->AddUsbDeviceObserver(this);
197 }
198 crostini::CrostiniExportImport::GetForProfile(profile_)->AddObserver(this);
199 crostini::CrostiniPortForwarder::GetForProfile(profile_)->AddObserver(this);
200
201 // Observe ADB sideloading device policy and react to its changes
202 adb_sideloading_device_policy_subscription_ =
203 chromeos::CrosSettings::Get()->AddSettingsObserver(
204 chromeos::kDeviceCrostiniArcAdbSideloadingAllowed,
205 base::BindRepeating(&CrostiniHandler::FetchCanChangeAdbSideloading,
206 weak_ptr_factory_.GetWeakPtr()));
207
208 // Observe ADB sideloading user policy and react to its changes
209 pref_change_registrar_.Init(profile_->GetPrefs());
210 pref_change_registrar_.Add(
211 crostini::prefs::kCrostiniArcAdbSideloadingUserPref,
212 base::BindRepeating(&CrostiniHandler::FetchCanChangeAdbSideloading,
213 weak_ptr_factory_.GetWeakPtr()));
214 }
215
OnJavascriptDisallowed()216 void CrostiniHandler::OnJavascriptDisallowed() {
217 auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_);
218 crostini_manager->RemoveCrostiniDialogStatusObserver(this);
219 crostini_manager->RemoveCrostiniContainerPropertiesObserver(this);
220 crostini_manager->RemoveContainerStartedObserver(this);
221 crostini_manager->RemoveContainerShutdownObserver(this);
222 crostini_manager->RemoveCrostiniMicSharingEnabledObserver(this);
223 if (chromeos::CrosUsbDetector::Get()) {
224 chromeos::CrosUsbDetector::Get()->RemoveUsbDeviceObserver(this);
225 }
226 crostini::CrostiniExportImport::GetForProfile(profile_)->RemoveObserver(this);
227 crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemoveObserver(
228 this);
229
230 adb_sideloading_device_policy_subscription_.reset();
231 pref_change_registrar_.RemoveAll();
232 }
233
HandleRequestCrostiniInstallerView(const base::ListValue * args)234 void CrostiniHandler::HandleRequestCrostiniInstallerView(
235 const base::ListValue* args) {
236 AllowJavascript();
237 crostini::CrostiniInstaller::GetForProfile(Profile::FromWebUI(web_ui()))
238 ->ShowDialog(crostini::CrostiniUISurface::kSettings);
239 }
240
HandleRequestRemoveCrostini(const base::ListValue * args)241 void CrostiniHandler::HandleRequestRemoveCrostini(const base::ListValue* args) {
242 AllowJavascript();
243 ShowCrostiniUninstallerView(Profile::FromWebUI(web_ui()),
244 crostini::CrostiniUISurface::kSettings);
245 }
246
HandleGetCrostiniSharedPathsDisplayText(const base::ListValue * args)247 void CrostiniHandler::HandleGetCrostiniSharedPathsDisplayText(
248 const base::ListValue* args) {
249 AllowJavascript();
250 CHECK_EQ(2U, args->GetList().size());
251 std::string callback_id = args->GetList()[0].GetString();
252 base::Value::ConstListView paths = args->GetList()[1].GetList();
253
254 base::ListValue texts;
255 for (size_t i = 0; i < paths.size(); ++i) {
256 texts.AppendString(file_manager::util::GetPathDisplayTextForSettings(
257 profile_, paths[i].GetString()));
258 }
259 ResolveJavascriptCallback(base::Value(callback_id), texts);
260 }
261
HandleRemoveCrostiniSharedPath(const base::ListValue * args)262 void CrostiniHandler::HandleRemoveCrostiniSharedPath(
263 const base::ListValue* args) {
264 AllowJavascript();
265 CHECK_EQ(3U, args->GetList().size());
266 std::string callback_id = args->GetList()[0].GetString();
267 std::string vm_name = args->GetList()[1].GetString();
268 std::string path = args->GetList()[2].GetString();
269
270 guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath(
271 vm_name, base::FilePath(path),
272 /*unpersist=*/true,
273 base::BindOnce(&CrostiniHandler::OnCrostiniSharedPathRemoved,
274 weak_ptr_factory_.GetWeakPtr(), callback_id, path));
275 }
276
OnCrostiniSharedPathRemoved(const std::string & callback_id,const std::string & path,bool result,const std::string & failure_reason)277 void CrostiniHandler::OnCrostiniSharedPathRemoved(
278 const std::string& callback_id,
279 const std::string& path,
280 bool result,
281 const std::string& failure_reason) {
282 if (!result) {
283 LOG(ERROR) << "Error unsharing " << path << ": " << failure_reason;
284 }
285 ResolveJavascriptCallback(base::Value(callback_id), base::Value(result));
286 }
287
288 namespace {
UsbDevicesToListValue(const std::vector<CrosUsbDeviceInfo> shared_usbs)289 base::ListValue UsbDevicesToListValue(
290 const std::vector<CrosUsbDeviceInfo> shared_usbs) {
291 base::ListValue usb_devices_list;
292 for (auto& device : shared_usbs) {
293 base::Value device_info(base::Value::Type::DICTIONARY);
294 device_info.SetStringKey("guid", device.guid);
295 device_info.SetStringKey("label", device.label);
296 bool shared = device.shared_vm_name == crostini::kCrostiniDefaultVmName;
297 device_info.SetBoolKey("shared", shared);
298 device_info.SetBoolKey("shareWillReassign",
299 device.shared_vm_name && !shared);
300 usb_devices_list.Append(std::move(device_info));
301 }
302 return usb_devices_list;
303 }
304
CrostiniDiskInfoToValue(std::unique_ptr<crostini::CrostiniDiskInfo> disk_info)305 base::Value CrostiniDiskInfoToValue(
306 std::unique_ptr<crostini::CrostiniDiskInfo> disk_info) {
307 base::Value disk_value(base::Value::Type::DICTIONARY);
308 if (!disk_info) {
309 disk_value.SetBoolKey("succeeded", false);
310 return disk_value;
311 }
312 disk_value.SetBoolKey("succeeded", true);
313 disk_value.SetBoolKey("canResize", disk_info->can_resize);
314 disk_value.SetBoolKey("isUserChosenSize", disk_info->is_user_chosen_size);
315 disk_value.SetBoolKey("isLowSpaceAvailable",
316 disk_info->is_low_space_available);
317 disk_value.SetIntKey("defaultIndex", disk_info->default_index);
318 base::Value ticks(base::Value::Type::LIST);
319 for (const auto& tick : disk_info->ticks) {
320 base::Value t(base::Value::Type::DICTIONARY);
321 t.SetDoubleKey("value", static_cast<double>(tick->value));
322 t.SetStringKey("ariaValue", tick->aria_value);
323 t.SetStringKey("label", tick->label);
324 ticks.Append(std::move(t));
325 }
326 disk_value.SetKey("ticks", std::move(ticks));
327 return disk_value;
328 }
329 } // namespace
330
HandleNotifyCrostiniSharedUsbDevicesPageReady(const base::ListValue * args)331 void CrostiniHandler::HandleNotifyCrostiniSharedUsbDevicesPageReady(
332 const base::ListValue* args) {
333 AllowJavascript();
334 OnUsbDevicesChanged();
335 }
336
HandleSetCrostiniUsbDeviceShared(const base::ListValue * args)337 void CrostiniHandler::HandleSetCrostiniUsbDeviceShared(
338 const base::ListValue* args) {
339 CHECK_EQ(2U, args->GetList().size());
340 const auto& args_list = args->GetList();
341 std::string guid = args_list[0].GetString();
342 bool shared = args_list[1].GetBool();
343
344 chromeos::CrosUsbDetector* detector = chromeos::CrosUsbDetector::Get();
345 if (!detector)
346 return;
347
348 if (shared) {
349 detector->AttachUsbDeviceToVm(crostini::kCrostiniDefaultVmName, guid,
350 base::DoNothing());
351 return;
352 }
353 detector->DetachUsbDeviceFromVm(crostini::kCrostiniDefaultVmName, guid,
354 base::DoNothing());
355 }
356
OnUsbDevicesChanged()357 void CrostiniHandler::OnUsbDevicesChanged() {
358 chromeos::CrosUsbDetector* detector = chromeos::CrosUsbDetector::Get();
359 DCHECK(detector); // This callback is called by the detector.
360 FireWebUIListener(
361 "crostini-shared-usb-devices-changed",
362 UsbDevicesToListValue(detector->GetDevicesSharableWithCrostini()));
363 }
364
HandleExportCrostiniContainer(const base::ListValue * args)365 void CrostiniHandler::HandleExportCrostiniContainer(
366 const base::ListValue* args) {
367 CHECK_EQ(0U, args->GetList().size());
368 crostini::CrostiniExportImport::GetForProfile(profile_)->ExportContainer(
369 web_ui()->GetWebContents());
370 }
371
HandleImportCrostiniContainer(const base::ListValue * args)372 void CrostiniHandler::HandleImportCrostiniContainer(
373 const base::ListValue* args) {
374 CHECK_EQ(0U, args->GetList().size());
375 crostini::CrostiniExportImport::GetForProfile(profile_)->ImportContainer(
376 web_ui()->GetWebContents());
377 }
378
HandleCrostiniInstallerStatusRequest(const base::ListValue * args)379 void CrostiniHandler::HandleCrostiniInstallerStatusRequest(
380 const base::ListValue* args) {
381 AllowJavascript();
382 CHECK_EQ(0U, args->GetList().size());
383 bool status = crostini::CrostiniManager::GetForProfile(profile_)
384 ->GetCrostiniDialogStatus(crostini::DialogType::INSTALLER);
385 OnCrostiniDialogStatusChanged(crostini::DialogType::INSTALLER, status);
386 }
387
HandleCrostiniExportImportOperationStatusRequest(const base::ListValue * args)388 void CrostiniHandler::HandleCrostiniExportImportOperationStatusRequest(
389 const base::ListValue* args) {
390 AllowJavascript();
391 CHECK_EQ(0U, args->GetList().size());
392 bool in_progress = crostini::CrostiniExportImport::GetForProfile(profile_)
393 ->GetExportImportOperationStatus();
394 OnCrostiniExportImportOperationStatusChanged(in_progress);
395 }
396
OnCrostiniDialogStatusChanged(crostini::DialogType dialog_type,bool status)397 void CrostiniHandler::OnCrostiniDialogStatusChanged(
398 crostini::DialogType dialog_type,
399 bool status) {
400 // It's technically possible for this to be called before Javascript is
401 // enabled, in which case we must not call FireWebUIListener
402 if (IsJavascriptAllowed()) {
403 // Other side listens with cr.addWebUIListener
404 switch (dialog_type) {
405 case crostini::DialogType::INSTALLER:
406 FireWebUIListener("crostini-installer-status-changed",
407 base::Value(status));
408 break;
409 case crostini::DialogType::UPGRADER:
410 FireWebUIListener("crostini-upgrader-status-changed",
411 base::Value(status));
412 break;
413 case crostini::DialogType::REMOVER:
414 FireWebUIListener("crostini-remover-status-changed",
415 base::Value(status));
416 break;
417 default:
418 NOTREACHED();
419 break;
420 }
421 }
422 }
423
OnContainerOsReleaseChanged(const crostini::ContainerId & container_id,bool can_upgrade)424 void CrostiniHandler::OnContainerOsReleaseChanged(
425 const crostini::ContainerId& container_id,
426 bool can_upgrade) {
427 if (crostini::CrostiniFeatures::Get()->IsContainerUpgradeUIAllowed(
428 profile_) &&
429 container_id == crostini::DefaultContainerId()) {
430 FireWebUIListener("crostini-container-upgrade-available-changed",
431 base::Value(can_upgrade));
432 }
433 }
434
OnQueryAdbSideload(SessionManagerClient::AdbSideloadResponseCode response_code,bool enabled)435 void CrostiniHandler::OnQueryAdbSideload(
436 SessionManagerClient::AdbSideloadResponseCode response_code,
437 bool enabled) {
438 if (response_code != SessionManagerClient::AdbSideloadResponseCode::SUCCESS) {
439 LOG(ERROR) << "Failed to query adb sideload status";
440 enabled = false;
441 }
442 bool need_powerwash =
443 response_code ==
444 SessionManagerClient::AdbSideloadResponseCode::NEED_POWERWASH;
445 // Other side listens with cr.addWebUIListener
446 FireWebUIListener("crostini-arc-adb-sideload-status-changed",
447 base::Value(enabled), base::Value(need_powerwash));
448 }
449
HandleEnableArcAdbRequest(const base::ListValue * args)450 void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) {
451 CHECK_EQ(0U, args->GetList().size());
452
453 crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(
454 profile_, base::BindOnce(&CrostiniHandler::OnCanEnableArcAdbSideloading,
455 weak_ptr_factory_.GetWeakPtr()));
456 }
457
OnCanEnableArcAdbSideloading(bool can_change_adb_sideloading)458 void CrostiniHandler::OnCanEnableArcAdbSideloading(
459 bool can_change_adb_sideloading) {
460 if (!can_change_adb_sideloading)
461 return;
462
463 LogEvent(CrostiniSettingsEvent::kEnableAdbSideloading);
464
465 PrefService* prefs = g_browser_process->local_state();
466 prefs->SetBoolean(prefs::kEnableAdbSideloadingRequested, true);
467 prefs->CommitPendingWrite();
468
469 chrome::AttemptRelaunch();
470 }
471
HandleDisableArcAdbRequest(const base::ListValue * args)472 void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) {
473 CHECK_EQ(0U, args->GetList().size());
474
475 crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(
476 profile_, base::BindOnce(&CrostiniHandler::OnCanDisableArcAdbSideloading,
477 weak_ptr_factory_.GetWeakPtr()));
478 }
479
OnCanDisableArcAdbSideloading(bool can_change_adb_sideloading)480 void CrostiniHandler::OnCanDisableArcAdbSideloading(
481 bool can_change_adb_sideloading) {
482 if (!can_change_adb_sideloading)
483 return;
484
485 LogEvent(CrostiniSettingsEvent::kDisableAdbSideloading);
486
487 PrefService* prefs = g_browser_process->local_state();
488 prefs->SetBoolean(prefs::kFactoryResetRequested, true);
489 prefs->CommitPendingWrite();
490
491 chromeos::PowerManagerClient::Get()->RequestRestart(
492 power_manager::REQUEST_RESTART_FOR_USER, "disable adb sideloading");
493 }
494
LaunchTerminal()495 void CrostiniHandler::LaunchTerminal() {
496 crostini::LaunchCrostiniApp(
497 profile_, crostini::kCrostiniTerminalSystemAppId,
498 display::Screen::GetScreen()->GetPrimaryDisplay().id());
499 }
500
HandleRequestContainerUpgradeView(const base::ListValue * args)501 void CrostiniHandler::HandleRequestContainerUpgradeView(
502 const base::ListValue* args) {
503 CHECK_EQ(0U, args->GetList().size());
504 chromeos::CrostiniUpgraderDialog::Show(
505 profile_,
506 base::BindOnce(&CrostiniHandler::LaunchTerminal,
507 weak_ptr_factory_.GetWeakPtr()),
508 // If the user cancels the upgrade, we won't need to restart Crostini and
509 // we don't want to run the launch closure which would launch Terminal.
510 /*only_run_launch_closure_on_restart=*/true);
511 }
512
OnCrostiniExportImportOperationStatusChanged(bool in_progress)513 void CrostiniHandler::OnCrostiniExportImportOperationStatusChanged(
514 bool in_progress) {
515 // Other side listens with cr.addWebUIListener
516 FireWebUIListener("crostini-export-import-operation-status-changed",
517 base::Value(in_progress));
518 }
519
HandleQueryArcAdbRequest(const base::ListValue * args)520 void CrostiniHandler::HandleQueryArcAdbRequest(const base::ListValue* args) {
521 AllowJavascript();
522 CHECK_EQ(0U, args->GetList().size());
523
524 chromeos::SessionManagerClient* client =
525 chromeos::SessionManagerClient::Get();
526 client->QueryAdbSideload(base::BindOnce(&CrostiniHandler::OnQueryAdbSideload,
527 weak_ptr_factory_.GetWeakPtr()));
528 }
529
HandleCanChangeArcAdbSideloadingRequest(const base::ListValue * args)530 void CrostiniHandler::HandleCanChangeArcAdbSideloadingRequest(
531 const base::ListValue* args) {
532 AllowJavascript();
533 CHECK_EQ(0U, args->GetList().size());
534
535 FetchCanChangeAdbSideloading();
536 }
537
FetchCanChangeAdbSideloading()538 void CrostiniHandler::FetchCanChangeAdbSideloading() {
539 crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(
540 profile_, base::BindOnce(&CrostiniHandler::OnCanChangeArcAdbSideloading,
541 weak_ptr_factory_.GetWeakPtr()));
542 }
543
OnCanChangeArcAdbSideloading(bool can_change_arc_adb_sideloading)544 void CrostiniHandler::OnCanChangeArcAdbSideloading(
545 bool can_change_arc_adb_sideloading) {
546 FireWebUIListener("crostini-can-change-arc-adb-sideload-changed",
547 base::Value(can_change_arc_adb_sideloading));
548 }
549
HandleCrostiniUpgraderDialogStatusRequest(const base::ListValue * args)550 void CrostiniHandler::HandleCrostiniUpgraderDialogStatusRequest(
551 const base::ListValue* args) {
552 AllowJavascript();
553 CHECK_EQ(0U, args->GetList().size());
554 bool is_open = crostini::CrostiniManager::GetForProfile(profile_)
555 ->GetCrostiniDialogStatus(crostini::DialogType::UPGRADER);
556 OnCrostiniDialogStatusChanged(crostini::DialogType::UPGRADER, is_open);
557 }
558
HandleCrostiniContainerUpgradeAvailableRequest(const base::ListValue * args)559 void CrostiniHandler::HandleCrostiniContainerUpgradeAvailableRequest(
560 const base::ListValue* args) {
561 AllowJavascript();
562
563 bool can_upgrade = crostini::ShouldAllowContainerUpgrade(profile_);
564 OnContainerOsReleaseChanged(crostini::DefaultContainerId(), can_upgrade);
565 }
566
OnActivePortsChanged(const base::ListValue & activePorts)567 void CrostiniHandler::OnActivePortsChanged(const base::ListValue& activePorts) {
568 // Other side listens with cr.addWebUIListener
569 FireWebUIListener("crostini-port-forwarder-active-ports-changed",
570 activePorts);
571 }
572
HandleAddCrostiniPortForward(const base::ListValue * args)573 void CrostiniHandler::HandleAddCrostiniPortForward(
574 const base::ListValue* args) {
575 CHECK_EQ(6U, args->GetList().size());
576
577 std::string callback_id = args->GetList()[0].GetString();
578 std::string vm_name = args->GetList()[1].GetString();
579 std::string container_name = args->GetList()[2].GetString();
580 int port_number = args->GetList()[3].GetInt();
581 int protocol_type = args->GetList()[4].GetInt();
582 std::string label = args->GetList()[5].GetString();
583
584 if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) {
585 OnPortForwardComplete(callback_id, false);
586 return;
587 }
588
589 crostini::CrostiniPortForwarder::GetForProfile(profile_)->AddPort(
590 crostini::ContainerId(std::move(vm_name), std::move(container_name)),
591 port_number,
592 static_cast<crostini::CrostiniPortForwarder::Protocol>(protocol_type),
593 std::move(label),
594 base::BindOnce(&CrostiniHandler::OnPortForwardComplete,
595 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
596 }
597
HandleRemoveCrostiniPortForward(const base::ListValue * args)598 void CrostiniHandler::HandleRemoveCrostiniPortForward(
599 const base::ListValue* args) {
600 CHECK_EQ(5U, args->GetSize());
601 std::string callback_id;
602 CHECK(args->GetString(0, &callback_id));
603 std::string vm_name;
604 CHECK(args->GetString(1, &vm_name));
605 std::string container_name;
606 CHECK(args->GetString(2, &container_name));
607 int port_number;
608 CHECK(args->GetInteger(3, &port_number));
609 int protocol_type;
610 CHECK(args->GetInteger(4, &protocol_type));
611
612 if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) {
613 OnPortForwardComplete(callback_id, false);
614 return;
615 }
616
617 crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemovePort(
618 crostini::ContainerId(std::move(vm_name), std::move(container_name)),
619 port_number,
620 static_cast<crostini::CrostiniPortForwarder::Protocol>(protocol_type),
621 base::Bind(&CrostiniHandler::OnPortForwardComplete,
622 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
623 }
624
HandleRemoveAllCrostiniPortForwards(const base::ListValue * args)625 void CrostiniHandler::HandleRemoveAllCrostiniPortForwards(
626 const base::ListValue* args) {
627 CHECK_EQ(2U, args->GetSize());
628 const auto& args_list = args->GetList();
629 std::string vm_name = args_list[0].GetString();
630 std::string container_name = args_list[1].GetString();
631
632 if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) {
633 return;
634 }
635
636 crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemoveAllPorts(
637 crostini::ContainerId(std::move(vm_name), std::move(container_name)));
638 }
639
HandleActivateCrostiniPortForward(const base::ListValue * args)640 void CrostiniHandler::HandleActivateCrostiniPortForward(
641 const base::ListValue* args) {
642 CHECK_EQ(5U, args->GetSize());
643
644 std::string callback_id;
645 CHECK(args->GetString(0, &callback_id));
646 std::string vm_name;
647 CHECK(args->GetString(1, &vm_name));
648 std::string container_name;
649 CHECK(args->GetString(2, &container_name));
650 int port_number;
651 CHECK(args->GetInteger(3, &port_number));
652 int protocol_type;
653 CHECK(args->GetInteger(4, &protocol_type));
654
655 if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) {
656 OnPortForwardComplete(callback_id, false);
657 return;
658 }
659
660 crostini::CrostiniPortForwarder::GetForProfile(profile_)->ActivatePort(
661 crostini::ContainerId(std::move(vm_name), std::move(container_name)),
662 port_number,
663 static_cast<crostini::CrostiniPortForwarder::Protocol>(protocol_type),
664 base::Bind(&CrostiniHandler::OnPortForwardComplete,
665 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
666 }
667
HandleDeactivateCrostiniPortForward(const base::ListValue * args)668 void CrostiniHandler::HandleDeactivateCrostiniPortForward(
669 const base::ListValue* args) {
670 CHECK_EQ(5U, args->GetSize());
671
672 std::string callback_id;
673 CHECK(args->GetString(0, &callback_id));
674 std::string vm_name;
675 CHECK(args->GetString(1, &vm_name));
676 std::string container_name;
677 CHECK(args->GetString(2, &container_name));
678 int port_number;
679 CHECK(args->GetInteger(3, &port_number));
680 int protocol_type;
681 CHECK(args->GetInteger(4, &protocol_type));
682
683 if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) {
684 OnPortForwardComplete(callback_id, false);
685 return;
686 }
687
688 crostini::CrostiniPortForwarder::GetForProfile(profile_)->DeactivatePort(
689 crostini::ContainerId(std::move(vm_name), std::move(container_name)),
690 port_number,
691 static_cast<crostini::CrostiniPortForwarder::Protocol>(protocol_type),
692 base::Bind(&CrostiniHandler::OnPortForwardComplete,
693 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
694 }
695
OnPortForwardComplete(std::string callback_id,bool success)696 void CrostiniHandler::OnPortForwardComplete(std::string callback_id,
697 bool success) {
698 ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
699 }
700
ResolveGetCrostiniDiskInfoCallback(const std::string & callback_id,std::unique_ptr<crostini::CrostiniDiskInfo> disk_info)701 void CrostiniHandler::ResolveGetCrostiniDiskInfoCallback(
702 const std::string& callback_id,
703 std::unique_ptr<crostini::CrostiniDiskInfo> disk_info) {
704 ResolveJavascriptCallback(base::Value(std::move(callback_id)),
705 CrostiniDiskInfoToValue(std::move(disk_info)));
706 }
707
HandleGetCrostiniDiskInfo(const base::ListValue * args)708 void CrostiniHandler::HandleGetCrostiniDiskInfo(const base::ListValue* args) {
709 AllowJavascript();
710 CHECK_EQ(3U, args->GetList().size());
711 std::string callback_id = args->GetList()[0].GetString();
712 std::string vm_name = args->GetList()[1].GetString();
713 bool full_info = args->GetList()[2].GetBool();
714 crostini::disk::GetDiskInfo(
715 base::BindOnce(&CrostiniHandler::ResolveGetCrostiniDiskInfoCallback,
716 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)),
717 profile_, std::move(vm_name), full_info);
718 }
719
HandleResizeCrostiniDisk(const base::ListValue * args)720 void CrostiniHandler::HandleResizeCrostiniDisk(const base::ListValue* args) {
721 CHECK_EQ(3U, args->GetList().size());
722 std::string callback_id = args->GetList()[0].GetString();
723 std::string vm_name = args->GetList()[1].GetString();
724 double bytes = args->GetList()[2].GetDouble();
725 crostini::disk::ResizeCrostiniDisk(
726 profile_, std::move(vm_name), bytes,
727 base::BindOnce(&CrostiniHandler::ResolveResizeCrostiniDiskCallback,
728 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
729 }
730
ResolveResizeCrostiniDiskCallback(const std::string & callback_id,bool succeeded)731 void CrostiniHandler::ResolveResizeCrostiniDiskCallback(
732 const std::string& callback_id,
733 bool succeeded) {
734 ResolveJavascriptCallback(base::Value(std::move(callback_id)),
735 base::Value(succeeded));
736 }
737
HandleCheckCrostiniMicSharingStatus(const base::ListValue * args)738 void CrostiniHandler::HandleCheckCrostiniMicSharingStatus(
739 const base::ListValue* args) {
740 CHECK_EQ(2U, args->GetList().size());
741 std::string callback_id = args->GetList()[0].GetString();
742 bool proposed_value = args->GetList()[1].GetBool();
743 bool requiresRestart =
744 crostini::IsCrostiniRunning(profile_) &&
745 crostini::CrostiniManager::GetForProfile(profile_)
746 ->crostini_mic_sharing_enabled() != proposed_value;
747
748 ResolveJavascriptCallback(base::Value(std::move(callback_id)),
749 base::Value(requiresRestart));
750 }
751
HandleGetCrostiniActivePorts(const base::ListValue * args)752 void CrostiniHandler::HandleGetCrostiniActivePorts(
753 const base::ListValue* args) {
754 AllowJavascript();
755 CHECK_EQ(1U, args->GetList().size());
756
757 std::string callback_id = args->GetList()[0].GetString();
758
759 ResolveJavascriptCallback(
760 base::Value(callback_id),
761 crostini::CrostiniPortForwarder::GetForProfile(profile_)
762 ->GetActivePorts());
763 }
764
HandleCheckCrostiniIsRunning(const base::ListValue * args)765 void CrostiniHandler::HandleCheckCrostiniIsRunning(
766 const base::ListValue* args) {
767 AllowJavascript();
768 CHECK_EQ(1U, args->GetList().size());
769
770 std::string callback_id = args->GetList()[0].GetString();
771
772 ResolveJavascriptCallback(base::Value(callback_id),
773 base::Value(crostini::IsCrostiniRunning(profile_)));
774 }
775
OnContainerStarted(const crostini::ContainerId & container_id)776 void CrostiniHandler::OnContainerStarted(
777 const crostini::ContainerId& container_id) {
778 FireWebUIListener("crostini-status-changed", base::Value(true));
779 }
780
OnContainerShutdown(const crostini::ContainerId & container_id)781 void CrostiniHandler::OnContainerShutdown(
782 const crostini::ContainerId& container_id) {
783 FireWebUIListener("crostini-status-changed", base::Value(false));
784 }
785
HandleShutdownCrostini(const base::ListValue * args)786 void CrostiniHandler::HandleShutdownCrostini(const base::ListValue* args) {
787 CHECK_EQ(0U, args->GetList().size());
788
789 const std::string vm_name = "termina";
790
791 crostini::CrostiniManager::GetForProfile(profile_)->StopVm(
792 std::move(vm_name), std::move(base::DoNothing()));
793 }
794
OnCrostiniMicSharingEnabledChanged(bool enabled)795 void CrostiniHandler::OnCrostiniMicSharingEnabledChanged(bool enabled) {
796 FireWebUIListener("crostini-mic-sharing-enabled-changed",
797 base::Value(enabled));
798 }
799
HandleSetCrostiniMicSharingEnabled(const base::ListValue * args)800 void CrostiniHandler::HandleSetCrostiniMicSharingEnabled(
801 const base::ListValue* args) {
802 CHECK_EQ(1U, args->GetList().size());
803 bool enabled = args->GetList()[0].GetBool();
804
805 crostini::CrostiniManager::GetForProfile(profile_)
806 ->SetCrostiniMicSharingEnabled(enabled);
807 }
808
HandleGetCrostiniMicSharingEnabled(const base::ListValue * args)809 void CrostiniHandler::HandleGetCrostiniMicSharingEnabled(
810 const base::ListValue* args) {
811 CHECK_EQ(1U, args->GetList().size());
812
813 std::string callback_id = args->GetList()[0].GetString();
814
815 ResolveJavascriptCallback(
816 base::Value(callback_id),
817 base::Value(crostini::CrostiniManager::GetForProfile(profile_)
818 ->crostini_mic_sharing_enabled()));
819 }
820
821 } // namespace settings
822 } // namespace chromeos
823