Commit 696fb33b authored by Wenzhao Zang's avatar Wenzhao Zang Committed by Commit Bot

cros: Introduce chrome.arcAppsPrivate API (Part I)

1) |getLaunchableApps| should only return launchable apps, but not all
   of the apps would necessarily be ready. (Non-ready apps becomes
   ready normally after ~10s after user session starts.)

2) |launchApp| can be used to launch non-ready apps. A spinner will be
   shown on the shelf and the app will launch after it becomes ready.

Bug: 819404
Change-Id: I6569043226f38b6905687defb4be95554a5e5511
Reviewed-on: https://chromium-review.googlesource.com/1141349
Commit-Queue: Wenzhao (Colin) Zang <wzang@chromium.org>
Reviewed-by: default avatarIstiaque Ahmed <lazyboy@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Reviewed-by: default avatarYury Khmel <khmel@chromium.org>
Reviewed-by: default avatarToni Barzic <tbarzic@chromium.org>
Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578509}
parent 2fd87fc9
...@@ -1754,6 +1754,8 @@ source_set("chromeos") { ...@@ -1754,6 +1754,8 @@ source_set("chromeos") {
"virtual_machines/virtual_machines_util.h", "virtual_machines/virtual_machines_util.h",
# Extension API implementations. # Extension API implementations.
"extensions/arc_apps_private_api.cc",
"extensions/arc_apps_private_api.h",
"extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.cc", "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.cc",
"extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h", "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h",
"extensions/echo_private_api.cc", "extensions/echo_private_api.cc",
......
// Copyright 2018 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.
#include "chrome/browser/chromeos/extensions/arc_apps_private_api.h"
#include <string>
#include <vector>
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "chrome/common/extensions/api/arc_apps_private.h"
#include "ui/events/event_constants.h"
ArcAppsPrivateGetLaunchableAppsFunction::
ArcAppsPrivateGetLaunchableAppsFunction() = default;
ArcAppsPrivateGetLaunchableAppsFunction::
~ArcAppsPrivateGetLaunchableAppsFunction() = default;
ExtensionFunction::ResponseAction
ArcAppsPrivateGetLaunchableAppsFunction::Run() {
ArcAppListPrefs* prefs =
ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()));
if (!prefs)
return RespondNow(Error("Not available"));
std::vector<extensions::api::arc_apps_private::AppInfo> result;
const std::vector<std::string> app_ids = prefs->GetAppIds();
for (const auto& app_id : app_ids) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
if (app_info && app_info->launchable) {
extensions::api::arc_apps_private::AppInfo app_info_result;
app_info_result.id = app_id;
result.push_back(std::move(app_info_result));
}
}
return RespondNow(ArgumentList(
extensions::api::arc_apps_private::GetLaunchableApps::Results::Create(
result)));
}
ArcAppsPrivateLaunchAppFunction::ArcAppsPrivateLaunchAppFunction() = default;
ArcAppsPrivateLaunchAppFunction::~ArcAppsPrivateLaunchAppFunction() = default;
ExtensionFunction::ResponseAction ArcAppsPrivateLaunchAppFunction::Run() {
std::unique_ptr<extensions::api::arc_apps_private::LaunchApp::Params> params(
extensions::api::arc_apps_private::LaunchApp::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
if (!ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context())))
return RespondNow(Error("Not available"));
if (!arc::LaunchApp(
browser_context(), params->app_id, ui::EF_NONE,
arc::UserInteractionType::APP_STARTED_FROM_EXTENSION_API)) {
return RespondNow(Error("Launch failed"));
}
return RespondNow(NoArguments());
}
\ No newline at end of file
// Copyright 2018 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.
#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_
#include "base/macros.h"
#include "extensions/browser/extension_function.h"
class ArcAppsPrivateGetLaunchableAppsFunction
: public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("arcAppsPrivate.getLaunchableApps",
ARCAPPSPRIVATE_GETLAUNCHABLEAPPS)
ArcAppsPrivateGetLaunchableAppsFunction();
protected:
~ArcAppsPrivateGetLaunchableAppsFunction() override;
// ExtensionFunction:
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateGetLaunchableAppsFunction);
};
class ArcAppsPrivateLaunchAppFunction : public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("arcAppsPrivate.launchApp",
ARCAPPSPRIVATE_LAUNCHAPP)
ArcAppsPrivateLaunchAppFunction();
protected:
~ArcAppsPrivateLaunchAppFunction() override;
// ExtensionFunction:
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateLaunchAppFunction);
};
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_
\ No newline at end of file
// Copyright 2018 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.
#include <memory>
#include "base/macros.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h"
#include "components/arc/test/connection_holder_util.h"
#include "components/arc/test/fake_app_instance.h"
namespace {
// Helper function to create a fake app instance and wait for the instance to be
// ready.
std::unique_ptr<arc::FakeAppInstance> CreateAppInstance(
ArcAppListPrefs* prefs) {
std::unique_ptr<arc::FakeAppInstance> app_instance =
std::make_unique<arc::FakeAppInstance>(prefs);
arc::ArcServiceManager::Get()->arc_bridge_service()->app()->SetInstance(
app_instance.get());
WaitForInstanceReady(
arc::ArcServiceManager::Get()->arc_bridge_service()->app());
return app_instance;
}
} // namespace
class ArcAppsPrivateApiTest : public extensions::ExtensionApiTest {
public:
ArcAppsPrivateApiTest() = default;
~ArcAppsPrivateApiTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
extensions::ExtensionApiTest::SetUpCommandLine(command_line);
arc::SetArcAvailableCommandLineForTesting(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
extensions::ExtensionApiTest::SetUpInProcessBrowserTestFixture();
arc::ArcSessionManager::SetUiEnabledForTesting(false);
std::unique_ptr<chromeos::FakeSessionManagerClient> session_manager_client =
std::make_unique<chromeos::FakeSessionManagerClient>();
session_manager_client->set_arc_available(true);
chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
std::move(session_manager_client));
}
void SetUpOnMainThread() override {
extensions::ExtensionApiTest::SetUpOnMainThread();
arc::SetArcPlayStoreEnabledForProfile(profile(), true);
}
private:
DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateApiTest);
};
IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetAppIdAndLaunchApp) {
ArcAppListPrefs* prefs = ArcAppListPrefs::Get(browser()->profile());
ASSERT_TRUE(prefs);
std::unique_ptr<arc::FakeAppInstance> app_instance = CreateAppInstance(prefs);
// Add one launchable app and one non-launchable app.
arc::mojom::AppInfo launchable_app("App_0", "Package_0", "Dummy_activity_0");
app_instance->SendRefreshAppList({launchable_app});
static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated(
0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1",
base::nullopt /* intent */);
// Stopping the service makes the app non-ready.
arc::ArcServiceManager::Get()->arc_bridge_service()->app()->CloseInstance(
app_instance.get());
EXPECT_EQ(0u, app_instance->launch_requests().size());
// Verify |chrome.arcAppsPrivate.getLaunchableApps| returns the id of
// the launchable app only. The JS test will attempt to launch the app.
EXPECT_TRUE(RunPlatformAppTestWithArg(
"arc_app_launcher",
ArcAppListPrefs::GetAppId("Package_0", "Dummy_activity_0").c_str()))
<< message_;
// Verify the app is not launched because it's not ready.
EXPECT_EQ(0u, app_instance->launch_requests().size());
// Restarting the service makes the app ready. Verify the app is launched
// successfully.
app_instance = CreateAppInstance(prefs);
app_instance->SendRefreshAppList({launchable_app});
ASSERT_EQ(1u, app_instance->launch_requests().size());
EXPECT_TRUE(app_instance->launch_requests()[0]->IsForApp(launchable_app));
}
\ No newline at end of file
...@@ -91,6 +91,10 @@ ...@@ -91,6 +91,10 @@
"dependencies": ["permission:appview"], "dependencies": ["permission:appview"],
"contexts": ["blessed_extension"] "contexts": ["blessed_extension"]
}, },
"arcAppsPrivate": {
"dependencies": ["permission:arcAppsPrivate"],
"contexts": ["blessed_extension"]
},
"autofillPrivate": [{ "autofillPrivate": [{
"dependencies": ["permission:autofillPrivate"], "dependencies": ["permission:autofillPrivate"],
"contexts": ["blessed_extension"] "contexts": ["blessed_extension"]
......
...@@ -47,6 +47,14 @@ ...@@ -47,6 +47,14 @@
"5107DE9024C329EEA9C9A72D94C16723790C6422" // Apps Developer Tool Dev. "5107DE9024C329EEA9C9A72D94C16723790C6422" // Apps Developer Tool Dev.
] ]
}, },
"arcAppsPrivate": {
"channel": "trunk",
"extension_types": ["platform_app"],
"whitelist": [
"D8524F339305807AB2439035774F746F739D0983" // Test platform app
// TODO(crbug.com/819404): Whitelist the Highlights app.
]
},
"autofillPrivate": { "autofillPrivate": {
"channel": "trunk", "channel": "trunk",
"extension_types": ["extension", "platform_app"], "extension_types": ["extension", "platform_app"],
......
...@@ -90,6 +90,7 @@ if (is_chromeos || is_mac || is_win) { ...@@ -90,6 +90,7 @@ if (is_chromeos || is_mac || is_win) {
if (is_chromeos) { if (is_chromeos) {
schema_sources_ += [ schema_sources_ += [
"arc_apps_private.idl",
"certificate_provider.idl", "certificate_provider.idl",
"certificate_provider_internal.idl", "certificate_provider_internal.idl",
"easy_unlock_private.idl", "easy_unlock_private.idl",
......
// Copyright 2018 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.
// Use the <code>chrome.arcAppsPrivate</code> API to manage ARC apps.
[platforms=("chromeos"),
implemented_in="chrome/browser/chromeos/extensions/arc_apps_private_api.h",
nodoc]
namespace arcAppsPrivate {
dictionary AppInfo {
// The app id.
DOMString id;
};
callback VoidCallback = void ();
callback GetLaunchableAppsCallback = void (AppInfo[] apps_info);
interface Functions {
// Returns info of the installed ARC apps that are launchable, including
// ready and non-ready apps.
static void getLaunchableApps(GetLaunchableAppsCallback callback);
// Launches the ARC app with |app_id|, which must be returned by
// |getLaunchableApps|. The app is launched immediately if it's ready,
// otherwise it will be launched when it becomes ready. The callback is
// called as soon as the launch is scheduled.
static void launchApp(DOMString app_id, optional VoidCallback callback);
};
};
\ No newline at end of file
...@@ -188,6 +188,7 @@ ChromeAPIPermissions::GetAllPermissions() const { ...@@ -188,6 +188,7 @@ ChromeAPIPermissions::GetAllPermissions() const {
{APIPermission::kResourcesPrivate, "resourcesPrivate", {APIPermission::kResourcesPrivate, "resourcesPrivate",
APIPermissionInfo::kFlagCannotBeOptional}, APIPermissionInfo::kFlagCannotBeOptional},
{APIPermission::kSafeBrowsingPrivate, "safeBrowsingPrivate"}, {APIPermission::kSafeBrowsingPrivate, "safeBrowsingPrivate"},
{APIPermission::kArcAppsPrivate, "arcAppsPrivate"},
// Full url access permissions. // Full url access permissions.
{APIPermission::kDebugger, "debugger", {APIPermission::kDebugger, "debugger",
......
...@@ -823,6 +823,7 @@ TEST(PermissionsTest, PermissionMessages) { ...@@ -823,6 +823,7 @@ TEST(PermissionsTest, PermissionMessages) {
// These are private. // These are private.
skip.insert(APIPermission::kAccessibilityPrivate); skip.insert(APIPermission::kAccessibilityPrivate);
skip.insert(APIPermission::kArcAppsPrivate);
skip.insert(APIPermission::kAutoTestPrivate); skip.insert(APIPermission::kAutoTestPrivate);
skip.insert(APIPermission::kBookmarkManagerPrivate); skip.insert(APIPermission::kBookmarkManagerPrivate);
skip.insert(APIPermission::kBrailleDisplayPrivate); skip.insert(APIPermission::kBrailleDisplayPrivate);
......
...@@ -1567,6 +1567,7 @@ test("browser_tests") { ...@@ -1567,6 +1567,7 @@ test("browser_tests") {
"../browser/chromeos/drive/drive_integration_service_browsertest.cc", "../browser/chromeos/drive/drive_integration_service_browsertest.cc",
"../browser/chromeos/extensions/accessibility_features_apitest.cc", "../browser/chromeos/extensions/accessibility_features_apitest.cc",
"../browser/chromeos/extensions/action_handlers/action_handlers_apitest.cc", "../browser/chromeos/extensions/action_handlers/action_handlers_apitest.cc",
"../browser/chromeos/extensions/arc_apps_private_apitest.cc",
"../browser/chromeos/extensions/default_keyboard_extension_browser_test.cc", "../browser/chromeos/extensions/default_keyboard_extension_browser_test.cc",
"../browser/chromeos/extensions/default_keyboard_extension_browser_test.h", "../browser/chromeos/extensions/default_keyboard_extension_browser_test.h",
"../browser/chromeos/extensions/echo_private_apitest.cc", "../browser/chromeos/extensions/echo_private_apitest.cc",
......
{
"key": "MIGqAgEAAiEAuTqZKXReYjq0LqmxcPXj5BhO4l43YRxiPm1A+Y6IqG8CAwEAAQIhAIwbSucUYqAij1AMg29STwFbL08USDm7SxcNgsW7aJzhAhEA6cjXcL5MFxl2geM2ZowcTQIRAMrUkXe2RyL2wuBDnV0VRasCEAkABWM+wDknbsuDTsXV4QUCEA5o66zFlmg1f8LPCu8CA+ECEC2UbOIhswH8ZhEictgqd5k=",
"name": "Launch Arc Apps test app",
"version": "1",
"manifest_version": 2,
"permissions": ["arcAppsPrivate"],
"app": {
"background": {
"scripts": ["test.js"]
}
}
}
\ No newline at end of file
// Copyright 2018 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.
chrome.app.runtime.onLaunched.addListener(function() {
var EXPECTED_APP_ID = null;
chrome.test.runTests([
function getConfig() {
chrome.test.getConfig(chrome.test.callbackPass(config => {
EXPECTED_APP_ID = config.customArg;
}));
},
function getAppIdAndLaunchApp() {
chrome.arcAppsPrivate.launchApp(
'invalid app id', chrome.test.callbackFail('Launch failed'));
chrome.test.assertTrue(!!EXPECTED_APP_ID);
chrome.arcAppsPrivate.getLaunchableApps(
chrome.test.callbackPass(appsInfo => {
chrome.test.assertEq([{id: EXPECTED_APP_ID}], appsInfo);
chrome.arcAppsPrivate.launchApp(
appsInfo[0].id, chrome.test.callbackPass());
}));
}
]);
});
\ No newline at end of file
...@@ -52,6 +52,9 @@ enum class UserInteractionType { ...@@ -52,6 +52,9 @@ enum class UserInteractionType {
// TODO(crbug.com/855381): Record this. // TODO(crbug.com/855381): Record this.
APP_CONTENT_WINDOW_INTERACTION = 11, APP_CONTENT_WINDOW_INTERACTION = 11,
// User started an app from chrome.arcAppsPrivate.launchApp.
APP_STARTED_FROM_EXTENSION_API = 12,
// The size of this enum; keep last. // The size of this enum; keep last.
SIZE, SIZE,
}; };
......
...@@ -1325,6 +1325,8 @@ enum HistogramValue { ...@@ -1325,6 +1325,8 @@ enum HistogramValue {
USERSPRIVATE_GETLOGINSTATUS = 1262, USERSPRIVATE_GETLOGINSTATUS = 1262,
FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE = 1263, FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE = 1263,
VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS = 1264, VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS = 1264,
ARCAPPSPRIVATE_GETLAUNCHABLEAPPS = 1265,
ARCAPPSPRIVATE_LAUNCHAPP = 1266,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -256,6 +256,7 @@ class APIPermission { ...@@ -256,6 +256,7 @@ class APIPermission {
kSafeBrowsingPrivate = 212, kSafeBrowsingPrivate = 212,
kFileSystemRequestDownloads = 213, kFileSystemRequestDownloads = 213,
kSystemPowerSource = 214, kSystemPowerSource = 214,
kArcAppsPrivate = 215,
// Last entry: Add new entries above and ensure to update the // Last entry: Add new entries above and ensure to update the
// "ExtensionPermission3" enum in tools/metrics/histograms/histograms.xml // "ExtensionPermission3" enum in tools/metrics/histograms/histograms.xml
// (by running update_extension_permission.py). // (by running update_extension_permission.py).
......
...@@ -16148,6 +16148,8 @@ Called by update_net_error_codes.py.--> ...@@ -16148,6 +16148,8 @@ Called by update_net_error_codes.py.-->
<int value="1262" label="USERSPRIVATE_GETLOGINSTATUS"/> <int value="1262" label="USERSPRIVATE_GETLOGINSTATUS"/>
<int value="1263" label="FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE"/> <int value="1263" label="FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE"/>
<int value="1264" label="VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS"/> <int value="1264" label="VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS"/>
<int value="1265" label="ARCAPPSPRIVATE_GETLAUNCHABLEAPPS"/>
<int value="1266" label="ARCAPPSPRIVATE_LAUNCHAPP"/>
</enum> </enum>
<enum name="ExtensionIconState"> <enum name="ExtensionIconState">
...@@ -16593,6 +16595,7 @@ Called by update_net_error_codes.py.--> ...@@ -16593,6 +16595,7 @@ Called by update_net_error_codes.py.-->
<int value="212" label="kSafeBrowsingPrivate"/> <int value="212" label="kSafeBrowsingPrivate"/>
<int value="213" label="kFileSystemRequestDownloads"/> <int value="213" label="kFileSystemRequestDownloads"/>
<int value="214" label="kSystemPowerSource"/> <int value="214" label="kSystemPowerSource"/>
<int value="215" label="kArcAppsPrivate"/>
</enum> </enum>
<enum name="ExtensionServiceVerifyAllSuccess"> <enum name="ExtensionServiceVerifyAllSuccess">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment