1 // Copyright 2017 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 "components/arc/arc_util.h"
6 
7 #include <algorithm>
8 #include <cstdio>
9 
10 #include "ash/public/cpp/app_types.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/feature_list.h"
14 #include "base/optional.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "chromeos/constants/chromeos_switches.h"
17 #include "chromeos/dbus/concierge_client.h"
18 #include "chromeos/dbus/dbus_thread_manager.h"
19 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
20 #include "chromeos/dbus/session_manager/session_manager_client.h"
21 #include "components/arc/arc_features.h"
22 #include "components/exo/shell_surface_util.h"
23 #include "components/user_manager/user_manager.h"
24 #include "ui/aura/client/aura_constants.h"
25 #include "ui/aura/window.h"
26 #include "ui/display/types/display_constants.h"
27 
28 namespace arc {
29 
30 namespace {
31 
32 // This is for finch. See also crbug.com/633704 for details.
33 // TODO(hidehiko): More comments of the intention how this works, when
34 // we unify the commandline flags.
35 const base::Feature kEnableArcFeature{"EnableARC",
36                                       base::FEATURE_DISABLED_BY_DEFAULT};
37 
38 // Possible values for --arc-availability flag.
39 constexpr char kAvailabilityNone[] = "none";
40 constexpr char kAvailabilityInstalled[] = "installed";
41 constexpr char kAvailabilityOfficiallySupported[] = "officially-supported";
42 constexpr char kAlwaysStartWithNoPlayStore[] =
43     "always-start-with-no-play-store";
44 
SetArcCpuRestrictionCallback(login_manager::ContainerCpuRestrictionState state,bool success)45 void SetArcCpuRestrictionCallback(
46     login_manager::ContainerCpuRestrictionState state,
47     bool success) {
48   if (success)
49     return;
50   const char* message =
51       (state == login_manager::CONTAINER_CPU_RESTRICTION_BACKGROUND)
52           ? "unprioritize"
53           : "prioritize";
54   LOG(ERROR) << "Failed to " << message << " ARC";
55 }
56 
OnSetArcVmCpuRestriction(base::Optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response)57 void OnSetArcVmCpuRestriction(
58     base::Optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response) {
59   if (!response) {
60     LOG(ERROR) << "Failed to call SetVmCpuRestriction";
61     return;
62   }
63   if (!response->success())
64     LOG(ERROR) << "SetVmCpuRestriction for ARCVM failed";
65 }
66 
DoSetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state,bool concierge_started)67 void DoSetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state,
68                               bool concierge_started) {
69   if (!concierge_started) {
70     LOG(ERROR) << "Concierge D-Bus service is not available";
71     return;
72   }
73 
74   auto* client = chromeos::DBusThreadManager::Get()->GetConciergeClient();
75   if (!client) {
76     LOG(ERROR) << "ConciergeClient is not available";
77     return;
78   }
79 
80   vm_tools::concierge::SetVmCpuRestrictionRequest request;
81   request.set_cpu_cgroup(vm_tools::concierge::CPU_CGROUP_ARCVM);
82   switch (cpu_restriction_state) {
83     case CpuRestrictionState::CPU_RESTRICTION_FOREGROUND:
84       request.set_cpu_restriction_state(
85           vm_tools::concierge::CPU_RESTRICTION_FOREGROUND);
86       break;
87     case CpuRestrictionState::CPU_RESTRICTION_BACKGROUND:
88       request.set_cpu_restriction_state(
89           vm_tools::concierge::CPU_RESTRICTION_BACKGROUND);
90       break;
91   }
92 
93   client->SetVmCpuRestriction(request,
94                               base::BindOnce(&OnSetArcVmCpuRestriction));
95 }
96 
SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state)97 void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state) {
98   auto* client = chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
99   if (!client) {
100     LOG(WARNING) << "DebugDaemonClient is not available";
101     return;
102   }
103   // TODO(wvk): Call StartConcierge() only when the service is not running.
104   client->StartConcierge(
105       base::BindOnce(&DoSetArcVmCpuRestriction, cpu_restriction_state));
106 }
107 
SetArcContainerCpuRestriction(CpuRestrictionState cpu_restriction_state)108 void SetArcContainerCpuRestriction(CpuRestrictionState cpu_restriction_state) {
109   if (!chromeos::SessionManagerClient::Get()) {
110     LOG(WARNING) << "SessionManagerClient is not available";
111     return;
112   }
113 
114   login_manager::ContainerCpuRestrictionState state;
115   switch (cpu_restriction_state) {
116     case CpuRestrictionState::CPU_RESTRICTION_FOREGROUND:
117       state = login_manager::CONTAINER_CPU_RESTRICTION_FOREGROUND;
118       break;
119     case CpuRestrictionState::CPU_RESTRICTION_BACKGROUND:
120       state = login_manager::CONTAINER_CPU_RESTRICTION_BACKGROUND;
121       break;
122   }
123   chromeos::SessionManagerClient::Get()->SetArcCpuRestriction(
124       state, base::BindOnce(SetArcCpuRestrictionCallback, state));
125 }
126 
127 }  // namespace
128 
IsArcAvailable()129 bool IsArcAvailable() {
130   const auto* command_line = base::CommandLine::ForCurrentProcess();
131 
132   if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
133     const std::string value =
134         command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
135     DCHECK(value == kAvailabilityNone || value == kAvailabilityInstalled ||
136            value == kAvailabilityOfficiallySupported)
137         << "Unknown flag value: " << value;
138     return value == kAvailabilityOfficiallySupported ||
139            (value == kAvailabilityInstalled &&
140             base::FeatureList::IsEnabled(kEnableArcFeature));
141   }
142 
143   // For transition, fallback to old flags.
144   // TODO(hidehiko): Remove this and clean up whole this function, when
145   // session_manager supports a new flag.
146   return command_line->HasSwitch(chromeos::switches::kEnableArc) ||
147          (command_line->HasSwitch(chromeos::switches::kArcAvailable) &&
148           base::FeatureList::IsEnabled(kEnableArcFeature));
149 }
150 
IsArcVmEnabled()151 bool IsArcVmEnabled() {
152   return base::CommandLine::ForCurrentProcess()->HasSwitch(
153       chromeos::switches::kEnableArcVm);
154 }
155 
ShouldArcAlwaysStart()156 bool ShouldArcAlwaysStart() {
157   const auto* command_line = base::CommandLine::ForCurrentProcess();
158   if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
159     return false;
160   return command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode) ==
161          kAlwaysStartWithNoPlayStore;
162 }
163 
ShouldArcAlwaysStartWithNoPlayStore()164 bool ShouldArcAlwaysStartWithNoPlayStore() {
165   return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
166              chromeos::switches::kArcStartMode) == kAlwaysStartWithNoPlayStore;
167 }
168 
ShouldShowOptInForTesting()169 bool ShouldShowOptInForTesting() {
170   return base::CommandLine::ForCurrentProcess()->HasSwitch(
171       chromeos::switches::kArcForceShowOptInUi);
172 }
173 
SetArcAlwaysStartWithoutPlayStoreForTesting()174 void SetArcAlwaysStartWithoutPlayStoreForTesting() {
175   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
176       chromeos::switches::kArcStartMode, kAlwaysStartWithNoPlayStore);
177 }
178 
IsArcKioskAvailable()179 bool IsArcKioskAvailable() {
180   const auto* command_line = base::CommandLine::ForCurrentProcess();
181 
182   if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
183     std::string value =
184         command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
185     if (value == kAvailabilityInstalled)
186       return true;
187     return IsArcAvailable();
188   }
189 
190   // TODO(hidehiko): Remove this when session_manager supports the new flag.
191   if (command_line->HasSwitch(chromeos::switches::kArcAvailable))
192     return true;
193 
194   // If not special kiosk device case, use general ARC check.
195   return IsArcAvailable();
196 }
197 
SetArcAvailableCommandLineForTesting(base::CommandLine * command_line)198 void SetArcAvailableCommandLineForTesting(base::CommandLine* command_line) {
199   command_line->AppendSwitchASCII(chromeos::switches::kArcAvailability,
200                                   kAvailabilityOfficiallySupported);
201 }
202 
IsArcKioskMode()203 bool IsArcKioskMode() {
204   return user_manager::UserManager::IsInitialized() &&
205          user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp();
206 }
207 
IsRobotOrOfflineDemoAccountMode()208 bool IsRobotOrOfflineDemoAccountMode() {
209   return user_manager::UserManager::IsInitialized() &&
210          (user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp() ||
211           user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
212 }
213 
IsArcAllowedForUser(const user_manager::User * user)214 bool IsArcAllowedForUser(const user_manager::User* user) {
215   if (!user) {
216     VLOG(1) << "No ARC for nullptr user.";
217     return false;
218   }
219 
220   // ARC is only supported for the following cases:
221   // - Users have Gaia accounts;
222   // - Active directory users;
223   // - ARC kiosk session;
224   // - Public Session users;
225   //   USER_TYPE_ARC_KIOSK_APP check is compatible with IsArcKioskMode()
226   //   above because ARC kiosk user is always the primary/active user of a
227   //   user session. The same for USER_TYPE_PUBLIC_ACCOUNT.
228   if (!user->HasGaiaAccount() && !user->IsActiveDirectoryUser() &&
229       user->GetType() != user_manager::USER_TYPE_ARC_KIOSK_APP &&
230       user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
231     VLOG(1) << "Users without GAIA or AD accounts, or not ARC kiosk apps are "
232                "not supported in ARC.";
233     return false;
234   }
235 
236   return true;
237 }
238 
IsArcOptInVerificationDisabled()239 bool IsArcOptInVerificationDisabled() {
240   return base::CommandLine::ForCurrentProcess()->HasSwitch(
241       chromeos::switches::kDisableArcOptInVerification);
242 }
243 
IsArcAppWindow(const aura::Window * window)244 bool IsArcAppWindow(const aura::Window* window) {
245   if (!window)
246     return false;
247   return window->GetProperty(aura::client::kAppType) ==
248          static_cast<int>(ash::AppType::ARC_APP);
249 }
250 
GetWindowTaskId(const aura::Window * window)251 int GetWindowTaskId(const aura::Window* window) {
252   if (!window)
253     return kNoTaskId;
254   const std::string* arc_app_id = exo::GetShellApplicationId(window);
255   if (!arc_app_id)
256     return kNoTaskId;
257   return GetTaskIdFromWindowAppId(*arc_app_id);
258 }
259 
GetTaskIdFromWindowAppId(const std::string & app_id)260 int GetTaskIdFromWindowAppId(const std::string& app_id) {
261   int task_id;
262   if (std::sscanf(app_id.c_str(), "org.chromium.arc.%d", &task_id) != 1)
263     return kNoTaskId;
264   return task_id;
265 }
266 
SetArcCpuRestriction(CpuRestrictionState cpu_restriction_state)267 void SetArcCpuRestriction(CpuRestrictionState cpu_restriction_state) {
268   // Ignore any calls to restrict the ARC container if the specified command
269   // line flag is set.
270   if (chromeos::switches::IsArcCpuRestrictionDisabled() &&
271       cpu_restriction_state == CpuRestrictionState::CPU_RESTRICTION_BACKGROUND)
272     return;
273 
274   if (IsArcVmEnabled()) {
275     SetArcVmCpuRestriction(cpu_restriction_state);
276   } else {
277     SetArcContainerCpuRestriction(cpu_restriction_state);
278   }
279 }
280 
IsArcForceCacheAppIcon()281 bool IsArcForceCacheAppIcon() {
282   return base::CommandLine::ForCurrentProcess()->HasSwitch(
283       chromeos::switches::kArcForceCacheAppIcons);
284 }
285 
IsArcDataCleanupOnStartRequested()286 bool IsArcDataCleanupOnStartRequested() {
287   return base::CommandLine::ForCurrentProcess()->HasSwitch(
288       chromeos::switches::kArcDataCleanupOnStart);
289 }
290 
IsArcAppSyncFlowDisabled()291 bool IsArcAppSyncFlowDisabled() {
292   return base::CommandLine::ForCurrentProcess()->HasSwitch(
293       chromeos::switches::kArcDisableAppSync);
294 }
295 
IsArcLocaleSyncDisabled()296 bool IsArcLocaleSyncDisabled() {
297   return base::CommandLine::ForCurrentProcess()->HasSwitch(
298       chromeos::switches::kArcDisableLocaleSync);
299 }
300 
IsArcPlayAutoInstallDisabled()301 bool IsArcPlayAutoInstallDisabled() {
302   return base::CommandLine::ForCurrentProcess()->HasSwitch(
303       chromeos::switches::kArcDisablePlayAutoInstall);
304 }
305 
306 // static
GetLcdDensityForDeviceScaleFactor(float device_scale_factor)307 int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) {
308   const auto* command_line = base::CommandLine::ForCurrentProcess();
309   if (command_line->HasSwitch(chromeos::switches::kArcScale)) {
310     const std::string dpi_str =
311         command_line->GetSwitchValueASCII(chromeos::switches::kArcScale);
312     int dpi;
313     if (base::StringToInt(dpi_str, &dpi))
314       return dpi;
315     VLOG(1) << "Invalid Arc scale set. Using default.";
316   }
317   // TODO(b/131884992): Remove the logic to update default lcd density once
318   // per-display-density is supported.
319   constexpr float kEpsilon = 0.001;
320   if (std::abs(device_scale_factor - display::kDsf_2_252) < kEpsilon)
321     return 280;
322   if (std::abs(device_scale_factor - 1.6f) < kEpsilon)
323     return 213;  // TVDPI
324   if (std::abs(device_scale_factor - display::kDsf_1_777) < kEpsilon)
325     return 240;  // HDPI
326   if (std::abs(device_scale_factor - display::kDsf_2_666) < kEpsilon)
327     return 320;  // XHDPI
328 
329   constexpr float kChromeScaleToAndroidScaleRatio = 0.75f;
330   constexpr int32_t kDefaultDensityDpi = 160;
331   return static_cast<int32_t>(
332       std::max(1.0f, device_scale_factor * kChromeScaleToAndroidScaleRatio) *
333       kDefaultDensityDpi);
334 }
335 
336 }  // namespace arc
337