Skip to content

Commit

Permalink
arc/gio: Enable Game Controls for different ARC versions
Browse files Browse the repository at this point in the history
Game Controls is available on non-O4C games that are not Game Controls
opt-out.

Since Optimized for ChromeOS(O4C) feature is not available on ARC-P and
it needs to distinguish Game Controls opt-out or O4C app, this CL
checks whether the game is Game Controls opt-out, then checks whether
the game is O4C game.

Android side: ag/24907285

Bug: b/275380306
Test: manual test
Test: unit_tests --gtest_filter=*ArcInputOverlay*
Change-Id: Ibf3a89877c893535e9f39b431f7fb48a66f1bbc3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4908849
Commit-Queue: Cici Ruan <cuicuiruan@google.com>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Reviewed-by: David Jacobo <djacobo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1209204}
  • Loading branch information
Cici Ruan authored and Chromium LUCI CQ committed Oct 13, 2023
1 parent c16d128 commit c0fb204
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 44 deletions.
9 changes: 7 additions & 2 deletions ash/components/arc/mojom/app.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Next MinVersion: 60
// Next MinVersion: 61

module arc.mojom;

Expand Down Expand Up @@ -412,7 +412,7 @@ interface AppHost {
};

// TODO(lhchavez): Migrate all request/response messages to Mojo.
// Next method ID: 42
// Next method ID: 43
// Deprecated method IDs: 0, 1, 2, 3, 4, 6, 9, 12, 13, 15, 17, 18, 19, 22, 31
interface AppInstance {
// Establishes full-duplex communication with the host.
Expand Down Expand Up @@ -560,4 +560,9 @@ interface AppInstance {
// declare an app category.
[MinVersion=53] GetAppCategory@41(string package_name) =>
(AppCategory category);

// Returns true if `package_name` is for a game app and not Game Controls
// opt-out.
[MinVersion=60] IsGameControlsApplicable@42(string package_name) =>
(bool is_gc_applicable);
};
11 changes: 8 additions & 3 deletions ash/components/arc/mojom/compatibility_mode.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Next MinVersion: 2
// Next MinVersion: 3

module arc.mojom;

Expand Down Expand Up @@ -30,7 +30,7 @@ enum ArcResizeLockState {
// mode to Android.
// Compatibility mode is a special set of WM policies to enforce restrictions
// on apps that are not optimized for large screens.
// Next Method ID: 2
// Next Method ID: 3
interface CompatibilityModeInstance {
// Synchronizes the resize lock state of the given package name to Android.
SetResizeLockState@0(string package_name,
Expand All @@ -40,4 +40,9 @@ interface CompatibilityModeInstance {
// |package_name| is a game app and doesn't have GIO opt-out.
[MinVersion=1] IsGioApplicable@1(string package_name) =>
(bool is_gio_applicable);
};

// Queries whether app of |package_name| is an O4C app. It returns true if
// |package_name| is an O4C app.
[MinVersion=2] IsOptimizedForCrosApp@2(string package_name) =>
(bool is_O4C_app);
};
12 changes: 12 additions & 0 deletions ash/components/arc/test/fake_app_instance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,18 @@ void FakeAppInstance::GetAppCategory(const std::string& package_name,
std::move(callback).Run(category);
}

void FakeAppInstance::IsGameControlsApplicable(
const std::string& package_name,
IsGameControlsApplicableCallback callback) {
bool applicable = false;
if (std::find(game_control_applicable_pkgs_.begin(),
game_control_applicable_pkgs_.end(),
package_name) != game_control_applicable_pkgs_.end()) {
applicable = true;
}
std::move(callback).Run(applicable);
}

void FakeAppInstance::LaunchIntentWithWindowInfo(
const std::string& intent_uri,
arc::mojom::WindowInfoPtr window_info) {
Expand Down
9 changes: 9 additions & 0 deletions ash/components/arc/test/fake_app_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ class FakeAppInstance : public mojom::AppInstance {
IsInstallableCallback callback) override;
void GetAppCategory(const std::string& package_name,
GetAppCategoryCallback callback) override;
void IsGameControlsApplicable(
const std::string& package_name,
IsGameControlsApplicableCallback callback) override;

// Methods to reply messages.
void SendRefreshAppList(const std::vector<mojom::AppInfoPtr>& apps);
Expand Down Expand Up @@ -257,6 +260,10 @@ class FakeAppInstance : public mojom::AppInstance {
pkg_name_to_app_category_[std::string(pkg_name)] = category;
}

void set_game_control_applicable_pkg(std::string_view pkg_name) {
game_control_applicable_pkgs_.emplace_back(std::string(pkg_name));
}

private:
using TaskIdToInfo = std::map<int32_t, std::unique_ptr<Request>>;

Expand Down Expand Up @@ -290,6 +297,8 @@ class FakeAppInstance : public mojom::AppInstance {
std::map<int, std::string> icon_responses_;
// Stores information for serving GetAppCategory calls.
std::map<std::string, mojom::AppCategory> pkg_name_to_app_category_;
// Stores information for serving IsGameControlsApplicable calls.
std::vector<std::string> game_control_applicable_pkgs_;

bool is_installable_ = false;

Expand Down
11 changes: 11 additions & 0 deletions ash/components/arc/test/fake_compatibility_mode_instance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,15 @@ void FakeCompatibilityModeInstance::IsGioApplicable(
std::move(callback).Run(is_gio_applicable_);
}

void FakeCompatibilityModeInstance::IsOptimizedForCrosApp(
const std::string& package_name,
IsOptimizedForCrosAppCallback callback) {
bool is_o4c = false;
if (std::find(o4c_pkgs_.begin(), o4c_pkgs_.end(), package_name) !=
o4c_pkgs_.end()) {
is_o4c = true;
}
std::move(callback).Run(is_o4c);
}

} // namespace arc
9 changes: 9 additions & 0 deletions ash/components/arc/test/fake_compatibility_mode_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@ class FakeCompatibilityModeInstance : public mojom::CompatibilityModeInstance {
mojom::ArcResizeLockState state) override;
void IsGioApplicable(const std::string& package_name,
IsGioApplicableCallback callback) override;
void IsOptimizedForCrosApp(const std::string& package_name,
IsOptimizedForCrosAppCallback callback) override;

void set_is_gio_applicable(bool is_gio_applicable) {
is_gio_applicable_ = is_gio_applicable;
}

void set_o4c_pkg(std::string_view pkg_name) {
o4c_pkgs_.emplace_back(std::string(pkg_name));
}

private:
bool is_gio_applicable_ = false;

// Stores information for serving IsOptimizedForCrosApp calls.
std::vector<std::string> o4c_pkgs_;
};

} // namespace arc
Expand Down
140 changes: 117 additions & 23 deletions chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,58 @@ void ArcInputOverlayManager::OnFinishReadDefaultData(
void ArcInputOverlayManager::OnDidCheckGioApplicable(
std::unique_ptr<TouchInjector> touch_injector,
bool is_gio_applicable) {
// For Optimized-for-Chromebook (O4C) games (which shouldn't apply GIO), GIO
// still applies if the associated `TouchInjector` is not empty. For example,
// users may have the mapping customization before games become O4C games and
// users may want to keep the previous mapping.
if (!is_gio_applicable && touch_injector->actions().empty()) {
if (is_gio_applicable) {
// Check if it is an O4C app from mojom instance.
auto* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
LOG(ERROR) << "Failed to get ArcServiceManager";
OnLoadingFinished(std::move(touch_injector));
return;
}
auto* compatibility_mode =
arc_service_manager->arc_bridge_service()->compatibility_mode();
if (!compatibility_mode || !compatibility_mode->IsConnected()) {
// This mojom is available for R and newer.
LOG(ERROR) << "No supported Android connection.";
OnLoadingFinished(std::move(touch_injector));
return;
}
auto* instance =
ARC_GET_INSTANCE_FOR_METHOD(compatibility_mode, IsOptimizedForCrosApp);
if (!instance) {
LOG(ERROR) << "IsOptimizedForCrosApp method for ARC is not available.";
OnLoadingFinished(std::move(touch_injector));
return;
}

std::string package_name = touch_injector->package_name();
VLOG(2) << "Check if pkg: " << package_name << " is O4C.";

instance->IsOptimizedForCrosApp(
package_name,
base::BindOnce(&ArcInputOverlayManager::OnDidCheckO4C, Unretained(this),
std::move(touch_injector)));
} else {
ResetForPendingTouchInjector(std::move(touch_injector));
}
}

void ArcInputOverlayManager::OnDidCheckO4C(
std::unique_ptr<TouchInjector> touch_injector,
bool is_o4c) {
bool remove_touch_injector = false;
if (is_o4c) {
if (touch_injector->actions().empty()) {
remove_touch_injector = true;
} else {
// Game Controls is still available but disabled if it is o4c because
// there is mapping set up before.
touch_injector->store_touch_injector_enable(false);
touch_injector->store_input_mapping_visible(false);
}
}

if (remove_touch_injector) {
ResetForPendingTouchInjector(std::move(touch_injector));
} else {
OnLoadingFinished(std::move(touch_injector));
Expand All @@ -454,31 +501,32 @@ void ArcInputOverlayManager::OnProtoDataAvailable(
return;
}

// Check if GIO is applicable from mojom instance.
auto* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
LOG(ERROR) << "Failed to get ArcServiceManager";
// Check if GIO is applicable from mojom instance, which means the app is a
// game app and is not GIO opt-out.
// ARC is only allowed for the primary user.
auto* profile = ProfileManager::GetPrimaryUserProfile();
DCHECK(arc::IsArcAllowedForProfile(profile));
auto* connection = ArcAppListPrefs::Get(profile)->app_connection_holder();
if (!connection) {
LOG(ERROR) << "Unable to get access to IsGameControlsApplicable for "
"nullptr |connection|.";
MayKeepTouchInjectorAfterError(std::move(touch_injector));
return;
}
auto* compatibility_mode =
arc_service_manager->arc_bridge_service()->compatibility_mode();
if (!compatibility_mode || !compatibility_mode->IsConnected()) {
LOG(ERROR) << "No supported Android connection.";
MayKeepTouchInjectorAfterError(std::move(touch_injector));
return;
}
auto* instance =
ARC_GET_INSTANCE_FOR_METHOD(compatibility_mode, IsGioApplicable);
if (!instance) {
LOG(ERROR) << "IsGioApplicable method for ARC is not available";
MayKeepTouchInjectorAfterError(std::move(touch_injector));

auto* app_instance =
ARC_GET_INSTANCE_FOR_METHOD(connection, IsGameControlsApplicable);
if (!app_instance) {
LOG(ERROR) << "IsGameControlsApplicable method for ARC is not available";
// Forward to old mojom call if new mojom call is not ready on ARC side.
CheckGioApplicableOld(std::move(touch_injector));
return;
}

std::string package_name = touch_injector->package_name();
VLOG(2) << "Check if GIO applicable on package: " << package_name;
instance->IsGioApplicable(
VLOG(2) << "Fetch if Game Controls is opt-out for game package: "
<< package_name;
app_instance->IsGameControlsApplicable(
package_name,
base::BindOnce(&ArcInputOverlayManager::OnDidCheckGioApplicable,
Unretained(this), std::move(touch_injector)));
Expand Down Expand Up @@ -667,6 +715,52 @@ void ArcInputOverlayManager::OnLoadingFinished(
RegisterFocusedWindow();
}

void ArcInputOverlayManager::CheckGioApplicableOld(
std::unique_ptr<TouchInjector> touch_injector) {
// Check if GIO is applicable from mojom instance.
auto* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
LOG(ERROR) << "Failed to get ArcServiceManager";
MayKeepTouchInjectorAfterError(std::move(touch_injector));
return;
}
auto* compatibility_mode =
arc_service_manager->arc_bridge_service()->compatibility_mode();
if (!compatibility_mode || !compatibility_mode->IsConnected()) {
LOG(ERROR) << "No supported Android connection.";
MayKeepTouchInjectorAfterError(std::move(touch_injector));
return;
}
auto* instance =
ARC_GET_INSTANCE_FOR_METHOD(compatibility_mode, IsGioApplicable);
if (!instance) {
LOG(ERROR) << "IsGioApplicable method for ARC is not available";
MayKeepTouchInjectorAfterError(std::move(touch_injector));
return;
}

std::string package_name = touch_injector->package_name();
VLOG(2) << "Check if GIO applicable on package: " << package_name;
instance->IsGioApplicable(
package_name,
base::BindOnce(&ArcInputOverlayManager::OnDidCheckGioApplicableOld,
Unretained(this), std::move(touch_injector)));
}

void ArcInputOverlayManager::OnDidCheckGioApplicableOld(
std::unique_ptr<TouchInjector> touch_injector,
bool is_gio_applicable) {
// For Optimized-for-Chromebook (O4C) games (which shouldn't apply GIO), GIO
// still applies if the associated `TouchInjector` is not empty. For example,
// users may have the mapping customization before games become O4C games and
// users may want to keep the previous mapping.
if (!is_gio_applicable && touch_injector->actions().empty()) {
ResetForPendingTouchInjector(std::move(touch_injector));
} else {
OnLoadingFinished(std::move(touch_injector));
}
}

void ArcInputOverlayManager::MayKeepTouchInjectorAfterError(
std::unique_ptr<TouchInjector> touch_injector) {
if (touch_injector->actions().empty()) {
Expand Down
12 changes: 11 additions & 1 deletion chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,13 @@ class ArcInputOverlayManager : public KeyedService,
std::unique_ptr<TouchInjector> touch_injector);
// Called when finishing reading default data.
void OnFinishReadDefaultData(std::unique_ptr<TouchInjector> touch_injector);
// Called after checking if GIO is applicable.
// Called after checking if GIO is applicable, which means the app is a game
// and doesn't have GIO opt-out.
void OnDidCheckGioApplicable(std::unique_ptr<TouchInjector> touch_injector,
bool is_gio_applicable);
// Called after checking if it is an O4C app.
void OnDidCheckO4C(std::unique_ptr<TouchInjector> touch_injector,
bool is_o4c);
// Apply the customized proto data.
void OnProtoDataAvailable(std::unique_ptr<TouchInjector> touch_injector,
std::unique_ptr<AppDataProto> proto);
Expand Down Expand Up @@ -137,6 +141,12 @@ class ArcInputOverlayManager : public KeyedService,
void MayKeepTouchInjectorAfterError(
std::unique_ptr<TouchInjector> touch_injector);

// TODO(b/303282089): Temporary functions before Android new mojom functions
// merged.
void CheckGioApplicableOld(std::unique_ptr<TouchInjector> touch_injector);
void OnDidCheckGioApplicableOld(std::unique_ptr<TouchInjector> touch_injector,
bool is_gio_applicable);

// Returns the game window if `window` is game dashboard window which is the
// window of `GameDashboardButton` or `GameDashboardMainMenu`. Otherwise,
// returns nullptr.
Expand Down

0 comments on commit c0fb204

Please sign in to comment.