chromium/chrome/browser/ui/ash/assistant/device_actions.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/ash/assistant/device_actions.h"

#include <string>
#include <utility>
#include <vector>

#include "ash/components/arc/mojom/intent_helper.mojom.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/bluetooth_config_service.h"
#include "base/functional/bind.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/app_list/arc/intent.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/technology_state_controller.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "ui/display/types/display_constants.h"

using ::ash::NetworkHandler;
using ::ash::NetworkTypePattern;
using ::ash::TechnologyStateController;
using ::ash::assistant::AndroidAppInfo;
using ::ash::assistant::AppStatus;

namespace {

constexpr char kIntentPrefix[] = "#Intent";
constexpr char kAction[] = "action";
constexpr char kPackage[] = "package";
constexpr char kLaunchFlags[] = "launchFlags";
constexpr char kEndSuffix[] = "end";

std::optional<std::string> GetActivity(const std::string& package_name) {
  auto* prefs = ArcAppListPrefs::Get(ProfileManager::GetActiveUserProfile());
  if (!prefs) {
    LOG(ERROR) << "ArcAppListPrefs is not available.";
    return std::nullopt;
  }
  std::string app_id = prefs->GetAppIdByPackageName(package_name);

  if (!app_id.empty()) {
    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
    return std::optional<std::string>(app_info->activity);
  }

  return std::nullopt;
}

std::string GetLaunchIntent(const AndroidAppInfo& app_info) {
  auto& package_name = app_info.package_name;
  if (app_info.intent.empty() || app_info.action.empty()) {
    // No action or data specified. Using launch intent from ARC.
    return arc::GetLaunchIntent(package_name,
                                GetActivity(package_name).value_or(""),
                                /*extra_params=*/{});
  }
  return base::StringPrintf("%s;%s;%s=%s;%s=0x%x;%s=%s;%s",
                            app_info.intent.c_str(), kIntentPrefix, kAction,
                            app_info.action.c_str(), kLaunchFlags,
                            arc::Intent::FLAG_ACTIVITY_NEW_TASK |
                                arc::Intent::FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
                            kPackage, package_name.c_str(), kEndSuffix);
}

std::vector<AndroidAppInfo> GetAppsInfo() {
  std::vector<AndroidAppInfo> android_apps_info;
  auto* prefs = ArcAppListPrefs::Get(ProfileManager::GetActiveUserProfile());
  if (!prefs) {
    LOG(ERROR) << "ArcAppListPrefs is not available.";
    return android_apps_info;
  }
  for (const auto& app_id : prefs->GetAppIds()) {
    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
    if (!app_info)
      continue;
    AndroidAppInfo android_app_info;
    android_app_info.package_name = app_info->package_name;
    auto package = prefs->GetPackage(app_info->package_name);
    if (package)
      android_app_info.version = package->package_version;
    android_app_info.localized_app_name = app_info->name;
    android_app_info.intent = app_info->intent_uri;
    android_apps_info.push_back(std::move(android_app_info));
  }
  return android_apps_info;
}

void NotifyAndroidAppListRefreshed(
    base::ObserverList<ash::assistant::AppListEventSubscriber>* subscribers) {
  std::vector<AndroidAppInfo> android_apps_info = GetAppsInfo();
  for (auto& subscriber : *subscribers)
    subscriber.OnAndroidAppListRefreshed(android_apps_info);
}

}  // namespace

DeviceActions::DeviceActions(std::unique_ptr<DeviceActionsDelegate> delegate)
    : delegate_(std::move(delegate)) {
  ash::GetBluetoothConfigService(
      remote_cros_bluetooth_config_.BindNewPipeAndPassReceiver());
}

DeviceActions::~DeviceActions() = default;

void DeviceActions::SetWifiEnabled(bool enabled) {
  NET_LOG(USER) << __func__ << ":" << enabled;
  NetworkHandler::Get()->technology_state_controller()->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), enabled,
      ash::network_handler::ErrorCallback());
}

void DeviceActions::SetBluetoothEnabled(bool enabled) {
  remote_cros_bluetooth_config_->SetBluetoothEnabledState(enabled);
}

void HandleScreenBrightnessCallback(
    DeviceActions::GetScreenBrightnessLevelCallback callback,
    std::optional<double> level) {
  if (level.has_value()) {
    std::move(callback).Run(true, level.value() / 100.0);
  } else {
    std::move(callback).Run(false, 0.0);
  }
}

void DeviceActions::GetScreenBrightnessLevel(
    DeviceActions::GetScreenBrightnessLevelCallback callback) {
  chromeos::PowerManagerClient::Get()->GetScreenBrightnessPercent(
      base::BindOnce(&HandleScreenBrightnessCallback, std::move(callback)));
}

void DeviceActions::SetScreenBrightnessLevel(double level, bool gradual) {
  power_manager::SetBacklightBrightnessRequest request;
  request.set_percent(level * 100);
  request.set_transition(
      gradual
          ? power_manager::SetBacklightBrightnessRequest_Transition_FAST
          : power_manager::SetBacklightBrightnessRequest_Transition_INSTANT);
  request.set_cause(
      power_manager::SetBacklightBrightnessRequest_Cause_USER_REQUEST);
  chromeos::PowerManagerClient::Get()->SetScreenBrightness(request);
}

void DeviceActions::SetNightLightEnabled(bool enabled) {
  const user_manager::User* const user =
      user_manager::UserManager::Get()->GetActiveUser();
  Profile* profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
  DCHECK(profile);
  // Simply toggle the user pref, which is being observed by ash's night
  // light controller.
  profile->GetPrefs()->SetBoolean(ash::prefs::kNightLightEnabled, enabled);
}

void DeviceActions::SetSwitchAccessEnabled(bool enabled) {
  const user_manager::User* const user =
      user_manager::UserManager::Get()->GetActiveUser();
  Profile* profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
  DCHECK(profile);
  profile->GetPrefs()->SetBoolean(ash::prefs::kAccessibilitySwitchAccessEnabled,
                                  enabled);
}

bool DeviceActions::OpenAndroidApp(const AndroidAppInfo& app_info) {
  auto status = delegate_->GetAndroidAppStatus(app_info.package_name);
  if (status != AppStatus::kAvailable)
    return false;

  auto* app = ARC_GET_INSTANCE_FOR_METHOD(
      arc::ArcServiceManager::Get()->arc_bridge_service()->app(),
      LaunchIntentWithWindowInfo);
  if (app) {
    arc::mojom::WindowInfoPtr window_info = arc::mojom::WindowInfo::New();
    window_info->display_id = display::kDefaultDisplayId;
    app->LaunchIntentWithWindowInfo(GetLaunchIntent(std::move(app_info)),
                                    std::move(window_info));
  } else {
    LOG(ERROR) << "Android container is not running. Discard request for launch"
               << app_info.package_name;
  }

  return app != nullptr;
}

AppStatus DeviceActions::GetAndroidAppStatus(const AndroidAppInfo& app_info) {
  return delegate_->GetAndroidAppStatus(app_info.package_name);
}

void DeviceActions::LaunchAndroidIntent(const std::string& intent) {
  auto* app = ARC_GET_INSTANCE_FOR_METHOD(
      arc::ArcServiceManager::Get()->arc_bridge_service()->app(),
      LaunchIntentWithWindowInfo);
  if (!app) {
    LOG(ERROR) << "Android container is not running.";
    return;
  }

  arc::mojom::WindowInfoPtr window_info = arc::mojom::WindowInfo::New();
  window_info->display_id = display::kDefaultDisplayId;
  app->LaunchIntentWithWindowInfo(intent, std::move(window_info));
}

void DeviceActions::AddAndFireAppListEventSubscriber(
    ash::assistant::AppListEventSubscriber* subscriber) {
  auto* prefs = ArcAppListPrefs::Get(ProfileManager::GetActiveUserProfile());
  if (prefs && prefs->package_list_initial_refreshed()) {
    std::vector<AndroidAppInfo> android_apps_info = GetAppsInfo();
    subscriber->OnAndroidAppListRefreshed(android_apps_info);
  }

  app_list_subscribers_.AddObserver(subscriber);

  if (prefs && !scoped_prefs_observations_.IsObservingSource(prefs))
    scoped_prefs_observations_.AddObservation(prefs);
}

void DeviceActions::RemoveAppListEventSubscriber(
    ash::assistant::AppListEventSubscriber* subscriber) {
  app_list_subscribers_.RemoveObserver(subscriber);
}

std::optional<std::string> DeviceActions::GetAndroidAppLaunchIntent(
    const AndroidAppInfo& app_info) {
  auto status = delegate_->GetAndroidAppStatus(app_info.package_name);
  if (status != AppStatus::kAvailable)
    return std::nullopt;

  return GetLaunchIntent(std::move(app_info));
}

void DeviceActions::OnPackageListInitialRefreshed() {
  NotifyAndroidAppListRefreshed(&app_list_subscribers_);
}

void DeviceActions::OnAppRegistered(const std::string& app_id,
                                    const ArcAppListPrefs::AppInfo& app_info) {
  NotifyAndroidAppListRefreshed(&app_list_subscribers_);
}

void DeviceActions::OnAppRemoved(const std::string& id) {
  NotifyAndroidAppListRefreshed(&app_list_subscribers_);
}