| // 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/apps/app_service/publishers/arc_apps.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "ash/components/arc/arc_util.h" |
| #include "ash/components/arc/metrics/arc_metrics_constants.h" |
| #include "ash/components/arc/metrics/arc_metrics_service.h" |
| #include "ash/components/arc/mojom/app_permissions.mojom.h" |
| #include "ash/components/arc/mojom/compatibility_mode.mojom.h" |
| #include "ash/components/arc/mojom/file_system.mojom.h" |
| #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/public/cpp/app_menu_constants.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/containers/contains.h" |
| #include "base/containers/flat_map.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "chrome/browser/apps/app_service/app_icon/dip_px_util.h" |
| #include "chrome/browser/apps/app_service/app_launch_params.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/apps/app_service/file_utils.h" |
| #include "chrome/browser/apps/app_service/intent_util.h" |
| #include "chrome/browser/apps/app_service/launch_utils.h" |
| #include "chrome/browser/apps/app_service/menu_util.h" |
| #include "chrome/browser/apps/app_service/publishers/arc_apps_factory.h" |
| #include "chrome/browser/apps/app_service/webapk/webapk_manager.h" |
| #include "chrome/browser/ash/apps/apk_web_app_service.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/arc/session/arc_session_manager.h" |
| #include "chrome/browser/ash/file_manager/path_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/app_list/arc/arc_app_icon.h" |
| #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" |
| #include "chrome/browser/web_applications/web_app_utils.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/grit/component_extension_resources.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/app_restore/app_launch_info.h" |
| #include "components/app_restore/full_restore_save_handler.h" |
| #include "components/app_restore/full_restore_utils.h" |
| #include "components/arc/common/intent_helper/arc_intent_helper_package.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "components/services/app_service/public/cpp/icon_types.h" |
| #include "components/services/app_service/public/cpp/intent.h" |
| #include "components/services/app_service/public/cpp/intent_filter.h" |
| #include "components/services/app_service/public/cpp/intent_filter_util.h" |
| #include "components/services/app_service/public/cpp/intent_util.h" |
| #include "components/services/app_service/public/cpp/permission.h" |
| #include "components/services/app_service/public/cpp/permission_utils.h" |
| #include "components/services/app_service/public/cpp/preferred_app.h" |
| #include "components/services/app_service/public/cpp/types_util.h" |
| #include "extensions/grit/extensions_browser_resources.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| |
| // TODO(crbug.com/826982): consider that, per khmel@, "App icon can be |
| // overwritten (setTaskDescription) or by assigning the icon for the app |
| // window. In this case some consumers (Shelf for example) switch to |
| // overwritten icon... IIRC this applies to shelf items and ArcAppWindow icon". |
| |
| namespace { |
| |
| void UpdateIconImage(apps::LoadIconCallback callback, apps::IconValuePtr iv) { |
| if (iv->icon_type == apps::IconType::kCompressed) { |
| ConvertUncompressedIconToCompressedIcon(std::move(iv), std::move(callback)); |
| return; |
| } |
| std::move(callback).Run(std::move(iv)); |
| } |
| |
| void OnArcAppIconCompletelyLoaded(apps::IconType icon_type, |
| int32_t size_hint_in_dip, |
| apps::IconEffects icon_effects, |
| apps::LoadIconCallback callback, |
| ArcAppIcon* icon) { |
| if (!icon) { |
| std::move(callback).Run(std::make_unique<apps::IconValue>()); |
| return; |
| } |
| |
| auto iv = std::make_unique<apps::IconValue>(); |
| iv->icon_type = icon_type; |
| iv->is_placeholder_icon = false; |
| |
| switch (icon_type) { |
| case apps::IconType::kCompressed: |
| [[fallthrough]]; |
| case apps::IconType::kUncompressed: |
| [[fallthrough]]; |
| case apps::IconType::kStandard: { |
| iv->uncompressed = |
| icon->is_adaptive_icon() |
| ? apps::CompositeImagesAndApplyMask(icon->foreground_image_skia(), |
| icon->background_image_skia()) |
| : apps::ApplyBackgroundAndMask(icon->image_skia()); |
| |
| if (icon_effects != apps::IconEffects::kNone) { |
| apps::ApplyIconEffects( |
| icon_effects, size_hint_in_dip, std::move(iv), |
| base::BindOnce(&UpdateIconImage, std::move(callback))); |
| return; |
| } |
| break; |
| } |
| case apps::IconType::kUnknown: |
| NOTREACHED(); |
| break; |
| } |
| |
| UpdateIconImage(std::move(callback), std::move(iv)); |
| } |
| |
| // TODO(crbug.com/1253250): Remove and use GetPermissionType |
| apps::mojom::PermissionType GetAppServicePermissionType( |
| arc::mojom::AppPermission arc_permission_type) { |
| switch (arc_permission_type) { |
| case arc::mojom::AppPermission::CAMERA: |
| return apps::mojom::PermissionType::kCamera; |
| case arc::mojom::AppPermission::LOCATION: |
| return apps::mojom::PermissionType::kLocation; |
| case arc::mojom::AppPermission::MICROPHONE: |
| return apps::mojom::PermissionType::kMicrophone; |
| case arc::mojom::AppPermission::NOTIFICATIONS: |
| return apps::mojom::PermissionType::kNotifications; |
| case arc::mojom::AppPermission::CONTACTS: |
| return apps::mojom::PermissionType::kContacts; |
| case arc::mojom::AppPermission::STORAGE: |
| return apps::mojom::PermissionType::kStorage; |
| } |
| } |
| |
| apps::PermissionType GetPermissionType( |
| arc::mojom::AppPermission arc_permission_type) { |
| switch (arc_permission_type) { |
| case arc::mojom::AppPermission::CAMERA: |
| return apps::PermissionType::kCamera; |
| case arc::mojom::AppPermission::LOCATION: |
| return apps::PermissionType::kLocation; |
| case arc::mojom::AppPermission::MICROPHONE: |
| return apps::PermissionType::kMicrophone; |
| case arc::mojom::AppPermission::NOTIFICATIONS: |
| return apps::PermissionType::kNotifications; |
| case arc::mojom::AppPermission::CONTACTS: |
| return apps::PermissionType::kContacts; |
| case arc::mojom::AppPermission::STORAGE: |
| return apps::PermissionType::kStorage; |
| } |
| } |
| |
| bool GetArcPermissionType( |
| apps::mojom::PermissionType app_service_permission_type, |
| arc::mojom::AppPermission& arc_permission) { |
| switch (app_service_permission_type) { |
| case apps::mojom::PermissionType::kCamera: |
| arc_permission = arc::mojom::AppPermission::CAMERA; |
| return true; |
| case apps::mojom::PermissionType::kLocation: |
| arc_permission = arc::mojom::AppPermission::LOCATION; |
| return true; |
| case apps::mojom::PermissionType::kMicrophone: |
| arc_permission = arc::mojom::AppPermission::MICROPHONE; |
| return true; |
| case apps::mojom::PermissionType::kNotifications: |
| arc_permission = arc::mojom::AppPermission::NOTIFICATIONS; |
| return true; |
| case apps::mojom::PermissionType::kContacts: |
| arc_permission = arc::mojom::AppPermission::CONTACTS; |
| return true; |
| case apps::mojom::PermissionType::kStorage: |
| arc_permission = arc::mojom::AppPermission::STORAGE; |
| return true; |
| case apps::mojom::PermissionType::kUnknown: |
| case apps::mojom::PermissionType::kPrinting: |
| case apps::mojom::PermissionType::kFileHandling: |
| return false; |
| } |
| } |
| |
| // TODO(crbug.com/1253250): Remove and use CreatePermissions |
| void UpdateAppPermissions( |
| const base::flat_map<arc::mojom::AppPermission, |
| arc::mojom::PermissionStatePtr>& new_permissions, |
| std::vector<apps::mojom::PermissionPtr>* permissions) { |
| for (const auto& new_permission : new_permissions) { |
| auto permission = apps::mojom::Permission::New(); |
| permission->permission_type = |
| GetAppServicePermissionType(new_permission.first); |
| permission->value = apps::mojom::PermissionValue::NewBoolValue( |
| new_permission.second->granted); |
| permission->is_managed = new_permission.second->managed; |
| |
| permissions->push_back(std::move(permission)); |
| } |
| } |
| |
| apps::Permissions CreatePermissions( |
| const base::flat_map<arc::mojom::AppPermission, |
| arc::mojom::PermissionStatePtr>& new_permissions) { |
| apps::Permissions permissions; |
| for (const auto& new_permission : new_permissions) { |
| permissions.push_back(std::make_unique<apps::Permission>( |
| GetPermissionType(new_permission.first), |
| std::make_unique<apps::PermissionValue>(new_permission.second->granted), |
| new_permission.second->managed)); |
| } |
| return permissions; |
| } |
| |
| absl::optional<arc::UserInteractionType> GetUserInterationType( |
| apps::LaunchSource launch_source) { |
| auto user_interaction_type = arc::UserInteractionType::NOT_USER_INITIATED; |
| switch (launch_source) { |
| // kUnknown is not set anywhere, this case is not valid. |
| case apps::LaunchSource::kUnknown: |
| return absl::nullopt; |
| case apps::LaunchSource::kFromChromeInternal: |
| user_interaction_type = arc::UserInteractionType::NOT_USER_INITIATED; |
| break; |
| case apps::LaunchSource::kFromAppListGrid: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER; |
| break; |
| case apps::LaunchSource::kFromAppListGridContextMenu: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_CONTEXT_MENU; |
| break; |
| case apps::LaunchSource::kFromAppListQuery: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SEARCH; |
| break; |
| case apps::LaunchSource::kFromAppListQueryContextMenu: |
| user_interaction_type = arc::UserInteractionType:: |
| APP_STARTED_FROM_LAUNCHER_SEARCH_CONTEXT_MENU; |
| break; |
| case apps::LaunchSource::kFromAppListRecommendation: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP; |
| break; |
| case apps::LaunchSource::kFromParentalControls: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_SETTINGS; |
| break; |
| case apps::LaunchSource::kFromShelf: |
| user_interaction_type = arc::UserInteractionType::APP_STARTED_FROM_SHELF; |
| break; |
| case apps::LaunchSource::kFromFileManager: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_FILE_MANAGER; |
| break; |
| case apps::LaunchSource::kFromLink: |
| user_interaction_type = arc::UserInteractionType::APP_STARTED_FROM_LINK; |
| break; |
| case apps::LaunchSource::kFromOmnibox: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_OMNIBOX; |
| break; |
| case apps::LaunchSource::kFromSharesheet: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_SHARESHEET; |
| break; |
| case apps::LaunchSource::kFromFullRestore: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_FULL_RESTORE; |
| break; |
| case apps::LaunchSource::kFromSmartTextContextMenu: |
| user_interaction_type = arc::UserInteractionType:: |
| APP_STARTED_FROM_SMART_TEXT_SELECTION_CONTEXT_MENU; |
| break; |
| case apps::LaunchSource::kFromOtherApp: |
| user_interaction_type = |
| arc::UserInteractionType::APP_STARTED_FROM_OTHER_APP; |
| break; |
| default: |
| NOTREACHED(); |
| return absl::nullopt; |
| } |
| return user_interaction_type; |
| } |
| |
| // Check if this intent filter only contains HTTP and HTTPS schemes. |
| bool IsHttpOrHttpsIntentFilter(const apps::IntentFilterPtr& intent_filter) { |
| for (const auto& condition : intent_filter->conditions) { |
| if (condition->condition_type != apps::ConditionType::kScheme) { |
| continue; |
| } |
| for (const auto& condition_value : condition->condition_values) { |
| if (condition_value->value != url::kHttpScheme && |
| condition_value->value != url::kHttpsScheme) { |
| return false; |
| } |
| } |
| return true; |
| } |
| // If there is no scheme |condition_type| found, return false. |
| return false; |
| } |
| |
| void AddPreferredApp(const std::string& app_id, |
| const apps::IntentFilterPtr& intent_filter, |
| apps::IntentPtr intent, |
| arc::ArcServiceManager* arc_service_manager, |
| ArcAppListPrefs* prefs) { |
| arc::mojom::IntentHelperInstance* instance = nullptr; |
| if (arc_service_manager) { |
| instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| AddPreferredApp); |
| } |
| if (!instance) { |
| return; |
| } |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| |
| // If |app_info| doesn't exist, we are trying to set preferences for a |
| // non-ARC app. Set the preferred app as the ARC intent helper package. |
| const std::string& package_name = |
| app_info ? app_info->package_name : arc::kArcIntentHelperPackageName; |
| |
| instance->AddPreferredApp( |
| package_name, |
| apps_util::ConvertAppServiceToArcIntentFilter(package_name, |
| intent_filter), |
| apps_util::ConvertAppServiceToArcIntent(std::move(intent))); |
| } |
| |
| void ResetVerifiedLinks( |
| const apps::IntentFilterPtr& intent_filter, |
| const apps::ReplacedAppPreferences& replaced_app_preferences, |
| arc::ArcServiceManager* arc_service_manager, |
| ArcAppListPrefs* prefs) { |
| if (!arc_service_manager) { |
| return; |
| } |
| std::vector<std::string> package_names; |
| |
| // Find the apps that needs to reset verified link domain status in ARC. |
| for (auto& entry : replaced_app_preferences) { |
| auto app_info = prefs->GetApp(entry.first); |
| if (!app_info) { |
| continue; |
| } |
| for (auto& intent_filter : entry.second) { |
| if (IsHttpOrHttpsIntentFilter(intent_filter)) { |
| package_names.push_back(app_info->package_name); |
| break; |
| } |
| } |
| } |
| |
| auto* instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| SetVerifiedLinks); |
| if (instance) { |
| instance->SetVerifiedLinks(package_names, /*always_open=*/false); |
| } |
| } |
| |
| bool ShouldShow(const ArcAppListPrefs::AppInfo& app_info) { |
| return app_info.show_in_launcher; |
| } |
| |
| void RequestDomainVerificationStatusUpdate(ArcAppListPrefs* prefs) { |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| arc::mojom::IntentHelperInstance* instance = nullptr; |
| |
| if (arc_service_manager) { |
| instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| RequestDomainVerificationStatusUpdate); |
| } |
| if (!instance) { |
| return; |
| } |
| instance->RequestDomainVerificationStatusUpdate(); |
| } |
| |
| arc::mojom::ActionType GetArcActionType(const std::string& action) { |
| if (action == apps_util::kIntentActionView) { |
| return arc::mojom::ActionType::VIEW; |
| } else if (action == apps_util::kIntentActionSend) { |
| return arc::mojom::ActionType::SEND; |
| } else if (action == apps_util::kIntentActionSendMultiple) { |
| return arc::mojom::ActionType::SEND_MULTIPLE; |
| } else if (action == apps_util::kIntentActionEdit) { |
| return arc::mojom::ActionType::EDIT; |
| } else { |
| return arc::mojom::ActionType::VIEW; |
| } |
| } |
| |
| // Constructs an OpenUrlsRequest to be passed to |
| // FileSystemInstance.OpenUrlsWithPermissionAndWindowInfo. |
| arc::mojom::OpenUrlsRequestPtr ConstructOpenUrlsRequest( |
| const apps::IntentPtr& intent, |
| const arc::mojom::ActivityNamePtr& activity, |
| const std::vector<GURL>& content_urls) { |
| arc::mojom::OpenUrlsRequestPtr request = arc::mojom::OpenUrlsRequest::New(); |
| request->action_type = GetArcActionType(intent->action); |
| request->activity_name = activity.Clone(); |
| DCHECK_EQ(content_urls.size(), intent->files.size()); |
| for (int i = 0; i < content_urls.size(); i++) { |
| auto content_url = content_urls[i]; |
| arc::mojom::ContentUrlWithMimeTypePtr url_with_type = |
| arc::mojom::ContentUrlWithMimeType::New(); |
| url_with_type->content_url = content_url; |
| DCHECK(intent->files[i]->mime_type.has_value() || |
| intent->mime_type.has_value()); |
| // Save the file's original mimetype to the URL if it exists. Otherwise, use |
| // the common intent mime type instead. |
| url_with_type->mime_type = intent->files[i]->mime_type.has_value() |
| ? intent->files[i]->mime_type.value() |
| : intent->mime_type.value(); |
| request->urls.push_back(std::move(url_with_type)); |
| } |
| if (intent->share_text.has_value() || intent->share_title.has_value() || |
| !intent->extras.empty()) { |
| request->extras = apps_util::CreateArcIntentExtras(intent); |
| } |
| return request; |
| } |
| |
| void OnContentUrlResolved(const base::FilePath& file_path, |
| const std::string& app_id, |
| int32_t event_flags, |
| apps::IntentPtr intent, |
| arc::mojom::ActivityNamePtr activity, |
| apps::WindowInfoPtr window_info, |
| base::OnceCallback<void(bool)> callback, |
| const std::vector<GURL>& content_urls) { |
| for (const auto& content_url : content_urls) { |
| if (!content_url.is_valid()) { |
| LOG(ERROR) << "Share files failed, file urls are not valid"; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| } |
| |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (!arc_service_manager) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| DCHECK(window_info); |
| int32_t session_id = window_info->window_id; |
| int64_t display_id = window_info->display_id; |
| |
| arc::mojom::FileSystemInstance* arc_file_system = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->file_system(), |
| OpenUrlsWithPermissionAndWindowInfo); |
| if (!arc_file_system) { |
| LOG(ERROR) << "Failed to open urls, ARC File System not found"; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| arc_file_system->OpenUrlsWithPermissionAndWindowInfo( |
| ConstructOpenUrlsRequest(intent, activity, content_urls), |
| apps::MakeArcWindowInfo(std::move(window_info)), base::DoNothing()); |
| |
| ::full_restore::SaveAppLaunchInfo( |
| file_path, |
| std::make_unique<app_restore::AppLaunchInfo>( |
| app_id, event_flags, std::move(intent), session_id, display_id)); |
| |
| std::move(callback).Run(/*success=*/true); |
| } |
| |
| // Sets the session id for |window_info|. If the full restore feature is |
| // disabled, or the session id has been set, returns |window_info|. Otherwise, |
| // fetches a new ARC session id, and sets to window_id for |window_info|. |
| apps::WindowInfoPtr SetSessionId(apps::WindowInfoPtr window_info) { |
| if (!window_info) { |
| window_info = |
| std::make_unique<apps::WindowInfo>(display::kInvalidDisplayId); |
| } |
| |
| if (window_info->window_id != -1) { |
| return window_info; |
| } |
| |
| window_info->window_id = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| return window_info; |
| } |
| |
| // Sets the session id for |window_info|. If the full restore feature is |
| // disabled, or the session id has been set, returns |window_info|. Otherwise, |
| // fetches a new ARC session id, and sets to window_id for |window_info|. |
| // TODO(crbug.com/1253250): Remove. Prefer the non mojom SetSessionId. |
| apps::mojom::WindowInfoPtr SetSessionId( |
| apps::mojom::WindowInfoPtr window_info) { |
| if (!window_info) { |
| window_info = apps::mojom::WindowInfo::New(); |
| window_info->display_id = display::kInvalidDisplayId; |
| } |
| |
| if (window_info->window_id != -1) { |
| return window_info; |
| } |
| |
| window_info->window_id = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| return window_info; |
| } |
| |
| absl::optional<bool> GetResizeLocked(ArcAppListPrefs* prefs, |
| const std::string& app_id) { |
| // Set null to resize lock state until the Mojo connection to ARC++ has been |
| // established. This prevents Chrome and ARC++ from having inconsistent |
| // states. |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (!arc_service_manager) { |
| return absl::nullopt; |
| } |
| |
| // If we don't have the connection (e.g. for non-supported Android versions), |
| // returns null. |
| auto* compatibility_mode = |
| arc_service_manager->arc_bridge_service()->compatibility_mode(); |
| if (!compatibility_mode->IsConnected()) { |
| return absl::nullopt; |
| } |
| |
| // Check if |SetResizeLockState| is available to see if Android is ready to |
| // be synchronized. Otherwise we need to hide the corresponding setting by |
| // returning null. |
| auto* instance = |
| ARC_GET_INSTANCE_FOR_METHOD(compatibility_mode, SetResizeLockState); |
| if (!instance) { |
| return absl::nullopt; |
| } |
| |
| auto resize_lock_state = prefs->GetResizeLockState(app_id); |
| switch (resize_lock_state) { |
| case arc::mojom::ArcResizeLockState::ON: |
| return true; |
| case arc::mojom::ArcResizeLockState::OFF: |
| return false; |
| case arc::mojom::ArcResizeLockState::UNDEFINED: |
| case arc::mojom::ArcResizeLockState::READY: |
| // FULLY_LOCKED means the resize-lock-related features are not available |
| // including the resizability toggle in the app management page. |
| case arc::mojom::ArcResizeLockState::FULLY_LOCKED: |
| return absl::nullopt; |
| } |
| } |
| |
| // TODO(crbug.com/1253250): Remove and use GetResizeLocked. |
| apps::mojom::OptionalBool IsResizeLocked(ArcAppListPrefs* prefs, |
| const std::string& app_id) { |
| auto resize_locked = GetResizeLocked(prefs, app_id); |
| if (!resize_locked.has_value()) { |
| return apps::mojom::OptionalBool::kUnknown; |
| } |
| |
| return resize_locked.value() ? apps::mojom::OptionalBool::kTrue |
| : apps::mojom::OptionalBool::kFalse; |
| } |
| |
| bool IsWebAppShellPackage(Profile* profile, |
| const ArcAppListPrefs::AppInfo& app_info) { |
| ash::ApkWebAppService* apk_web_app_service = |
| ash::ApkWebAppService::Get(profile); |
| return apk_web_app_service && |
| apk_web_app_service->IsWebAppShellPackage(app_info.package_name); |
| } |
| |
| bool IntentHasFilesAndMimeTypes(const apps::IntentPtr& intent) { |
| if (intent->files.empty()) { |
| return false; |
| } |
| bool all_files_have_mime_type = base::ranges::all_of( |
| intent->files, |
| [](apps::IntentFilePtr& file) { return file->mime_type.has_value(); }); |
| return all_files_have_mime_type || intent->mime_type.has_value(); |
| } |
| |
| // TODO(crbug.com/1253250): Remove. Prefer the non mojom |
| // IntentHasFilesAndMimeTypes. |
| bool IntentHasFilesAndMimeTypes(const apps::mojom::IntentPtr& intent) { |
| if (!intent->files.has_value()) { |
| return false; |
| } |
| bool all_files_have_mime_type = base::ranges::all_of( |
| *intent->files, [](apps::mojom::IntentFilePtr& file) { |
| return file->mime_type.has_value(); |
| }); |
| return all_files_have_mime_type || intent->mime_type.has_value(); |
| } |
| } // namespace |
| |
| namespace apps { |
| |
| // static |
| ArcApps* ArcApps::Get(Profile* profile) { |
| return ArcAppsFactory::GetForProfile(profile); |
| } |
| |
| ArcApps::ArcApps(AppServiceProxy* proxy) |
| : AppPublisher(proxy), |
| profile_(proxy->profile()), |
| arc_icon_once_loader_(profile_) {} |
| |
| ArcApps::~ArcApps() = default; |
| |
| void ArcApps::Initialize() { |
| if (!arc::IsArcAllowedForProfile(profile_) || |
| (arc::ArcServiceManager::Get() == nullptr)) { |
| return; |
| } |
| |
| mojo::Remote<apps::mojom::AppService>& app_service = proxy()->AppService(); |
| if (!app_service.is_bound()) { |
| return; |
| } |
| |
| // Make some observee-observer connections. |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| prefs->AddObserver(this); |
| proxy()->SetArcIsRegistered(); |
| |
| auto* intent_helper_bridge = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper_bridge) { |
| intent_helper_bridge->SetAdaptiveIconDelegate( |
| &arc_activity_adaptive_icon_impl_); |
| arc_intent_helper_observation_.Observe(intent_helper_bridge); |
| } |
| |
| // There is no MessageCenterController for unit tests, so observe when the |
| // MessageCenterController is created in production code. |
| if (ash::ArcNotificationsHostInitializer::Get()) { |
| notification_initializer_observation_.Observe( |
| ash::ArcNotificationsHostInitializer::Get()); |
| } |
| |
| auto* instance_registry = &proxy()->InstanceRegistry(); |
| if (instance_registry) { |
| instance_registry_observation_.Observe(instance_registry); |
| } |
| |
| if (web_app::AreWebAppsEnabled(profile_)) { |
| web_apk_manager_ = std::make_unique<apps::WebApkManager>(profile_); |
| } |
| |
| PublisherBase::Initialize(app_service, apps::mojom::AppType::kArc); |
| |
| RegisterPublisher(AppType::kArc); |
| |
| std::vector<AppPtr> apps; |
| for (const auto& app_id : prefs->GetAppIds()) { |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (app_info) { |
| apps.push_back(CreateApp(prefs, app_id, *app_info)); |
| } |
| } |
| AppPublisher::Publish(std::move(apps), AppType::kArc, |
| /*should_notify_initialized=*/true); |
| } |
| |
| void ArcApps::Shutdown() { |
| // Disconnect the observee-observer connections that we made during the |
| // constructor. |
| // |
| // This isn't entirely correct. The object returned by |
| // ArcAppListPrefs::Get(some_profile) can vary over the lifetime of that |
| // profile. If it changed, we'll try to disconnect from different |
| // ArcAppListPrefs-related objects than the ones we connected to, at the time |
| // of this object's construction. |
| // |
| // Even so, this is probably harmless, assuming that calling |
| // foo->RemoveObserver(bar) is a no-op (and e.g. does not crash) if bar |
| // wasn't observing foo in the first place, and assuming that the dangling |
| // observee-observer connection on the old foo's are never followed again. |
| // |
| // To fix this properly, we would probably need to add something like an |
| // OnArcAppListPrefsWillBeDestroyed method to ArcAppListPrefs::Observer, and |
| // in this class's implementation of that method, disconnect. Furthermore, |
| // when the new ArcAppListPrefs object is created, we'll have to somehow be |
| // notified so we can re-connect this object as an observer. |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (prefs) { |
| prefs->RemoveObserver(this); |
| } |
| arc_icon_once_loader_.StopObserving(prefs); |
| |
| auto* intent_helper_bridge = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper_bridge) { |
| intent_helper_bridge->SetAdaptiveIconDelegate(nullptr); |
| } |
| |
| arc_intent_helper_observation_.Reset(); |
| } |
| |
| void ArcApps::LoadIcon(const std::string& app_id, |
| const IconKey& icon_key, |
| IconType icon_type, |
| int32_t size_hint_in_dip, |
| bool allow_placeholder_icon, |
| apps::LoadIconCallback callback) { |
| if (icon_type == IconType::kUnknown) { |
| std::move(callback).Run(std::make_unique<IconValue>()); |
| return; |
| } |
| IconEffects icon_effects = static_cast<IconEffects>(icon_key.icon_effects); |
| |
| // Treat the Play Store as a special case, loading an icon defined by a |
| // resource instead of asking the Android VM (or the cache of previous |
| // responses from the Android VM). Presumably this is for bootstrapping: |
| // the Play Store icon (the UI for enabling and installing Android apps) |
| // should be showable even before the user has installed their first |
| // Android app and before bringing up an Android VM for the first time. |
| if (app_id == arc::kPlayStoreAppId) { |
| LoadPlayStoreIcon(icon_type, size_hint_in_dip, icon_effects, |
| std::move(callback)); |
| } else { |
| const ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile_); |
| DCHECK(arc_prefs); |
| |
| // If the app has been removed, immediately terminate the icon request since |
| // it can't possibly succeed. |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| arc_prefs->GetApp(app_id); |
| if (!app_info) { |
| std::move(callback).Run(std::make_unique<IconValue>()); |
| return; |
| } |
| |
| arc_icon_once_loader_.LoadIcon( |
| app_id, size_hint_in_dip, icon_type, |
| base::BindOnce(&OnArcAppIconCompletelyLoaded, icon_type, |
| size_hint_in_dip, icon_effects, std::move(callback))); |
| } |
| } |
| |
| void ArcApps::Launch(const std::string& app_id, |
| int32_t event_flags, |
| LaunchSource launch_source, |
| WindowInfoPtr window_info) { |
| auto user_interaction_type = GetUserInterationType(launch_source); |
| if (!user_interaction_type.has_value()) { |
| return; |
| } |
| |
| if (app_id == arc::kPlayStoreAppId && |
| apps_util::IsHumanLaunch(launch_source)) { |
| arc::RecordPlayStoreLaunchWithinAWeek(profile_->GetPrefs(), |
| /*launched=*/true); |
| } |
| |
| auto new_window_info = SetSessionId(std::move(window_info)); |
| int32_t session_id = new_window_info->window_id; |
| int64_t display_id = new_window_info->display_id; |
| |
| arc::LaunchApp(profile_, app_id, event_flags, user_interaction_type.value(), |
| MakeArcWindowInfo(std::move(new_window_info))); |
| |
| full_restore::SaveAppLaunchInfo( |
| profile_->GetPath(), std::make_unique<app_restore::AppLaunchInfo>( |
| app_id, event_flags, session_id, display_id)); |
| } |
| |
| void ArcApps::LaunchAppWithIntent(const std::string& app_id, |
| int32_t event_flags, |
| IntentPtr intent, |
| LaunchSource launch_source, |
| WindowInfoPtr window_info, |
| base::OnceCallback<void(bool)> callback) { |
| auto user_interaction_type = GetUserInterationType(launch_source); |
| if (!user_interaction_type.has_value()) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| if (app_id == arc::kPlayStoreAppId && |
| apps_util::IsHumanLaunch(launch_source)) { |
| arc::RecordPlayStoreLaunchWithinAWeek(profile_->GetPrefs(), |
| /*launched=*/true); |
| } |
| |
| arc::ArcMetricsService::RecordArcUserInteraction( |
| profile_, user_interaction_type.value()); |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| LOG(ERROR) << "Launch App failed, could not find app with id " << app_id; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| if (app_info->ready) { |
| arc::mojom::ActivityNamePtr activity = arc::mojom::ActivityName::New(); |
| activity->package_name = app_info->package_name; |
| if (intent->activity_name.has_value() && |
| !intent->activity_name.value().empty()) { |
| activity->activity_name = intent->activity_name.value(); |
| } |
| |
| auto new_window_info = SetSessionId(std::move(window_info)); |
| int32_t session_id = new_window_info->window_id; |
| int64_t display_id = new_window_info->display_id; |
| |
| // Check if the intent has files, and whether the intent has a mime type or |
| // all the individual files have mime types. |
| if (IntentHasFilesAndMimeTypes(intent)) { |
| std::vector<GURL> file_urls; |
| for (const auto& file : intent->files) { |
| file_urls.push_back(file->url); |
| } |
| arc::ConvertToContentUrlsAndShare( |
| profile_, apps::GetFileSystemURL(profile_, file_urls), |
| base::BindOnce(&OnContentUrlResolved, profile_->GetPath(), app_id, |
| event_flags, std::move(intent), std::move(activity), |
| std::move(new_window_info), std::move(callback))); |
| return; |
| } |
| |
| auto intent_for_full_restore = intent->Clone(); |
| |
| std::string intent_str = |
| apps_util::CreateLaunchIntent(app_info->package_name, intent); |
| if (!intent_str.empty()) { |
| // If |intent| can be converted to a string, call the Launch interface. |
| if (!arc::LaunchAppWithIntent( |
| profile_, app_id, intent_str, event_flags, |
| user_interaction_type.value(), |
| MakeArcWindowInfo(std::move(new_window_info)))) { |
| VLOG(2) << "Failed to launch app: " + app_id + "."; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| } else { |
| // If |intent| can't be converted to a string, call the HandleIntent |
| // interface. |
| auto arc_intent = |
| apps_util::ConvertAppServiceToArcIntent(std::move(intent)); |
| |
| if (!arc_intent) { |
| LOG(ERROR) << "Launch App failed, launch intent is not valid"; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (!arc_service_manager) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| arc::mojom::IntentHelperInstance* instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| HandleIntentWithWindowInfo); |
| if (instance) { |
| instance->HandleIntentWithWindowInfo( |
| std::move(arc_intent), std::move(activity), |
| MakeArcWindowInfo(std::move(new_window_info))); |
| } else { |
| instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| HandleIntent); |
| if (!instance) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| instance->HandleIntent(std::move(arc_intent), std::move(activity)); |
| } |
| } |
| |
| prefs->SetLastLaunchTime(app_id); |
| |
| full_restore::SaveAppLaunchInfo( |
| profile_->GetPath(), |
| std::make_unique<app_restore::AppLaunchInfo>( |
| app_id, event_flags, std::move(intent_for_full_restore), session_id, |
| display_id)); |
| std::move(callback).Run(/*success=*/true); |
| return; |
| } |
| |
| if (arc::IsArcPlayStoreEnabledForProfile(profile_)) { |
| // Handle the case when default app tries to re-activate OptIn flow. |
| if (arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile_) && |
| !arc::ArcSessionManager::Get()->enable_requested() && |
| prefs->IsDefault(app_id)) { |
| arc::SetArcPlayStoreEnabledForProfile(profile_, true); |
| // PlayStore item has special handling for shelf controllers. In order |
| // to avoid unwanted initial animation for PlayStore item do not create |
| // deferred launch request when PlayStore item enables Google Play |
| // Store. |
| if (app_id == arc::kPlayStoreAppId) { |
| prefs->SetLastLaunchTime(app_id); |
| std::move(callback).Run(/*success=*/true); |
| return; |
| } |
| } |
| } else { |
| if (prefs->IsDefault(app_id)) { |
| // The setting can fail if the preference is managed. However, the |
| // caller is responsible to not call this function in such case. DCHECK |
| // is here to prevent possible mistake. |
| if (!arc::SetArcPlayStoreEnabledForProfile(profile_, true)) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| DCHECK(arc::IsArcPlayStoreEnabledForProfile(profile_)); |
| |
| // PlayStore item has special handling for shelf controllers. In order |
| // to avoid unwanted initial animation for PlayStore item do not create |
| // deferred launch request when PlayStore item enables Google Play |
| // Store. |
| if (app_id == arc::kPlayStoreAppId) { |
| prefs->SetLastLaunchTime(app_id); |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| } else { |
| // Only reachable when ARC always starts. |
| DCHECK(arc::ShouldArcAlwaysStart()); |
| } |
| } |
| std::move(callback).Run(/*success=*/true); |
| } |
| |
| void ArcApps::LaunchAppWithParams(AppLaunchParams&& params, |
| LaunchCallback callback) { |
| auto event_flags = apps::GetEventFlags(params.disposition, |
| /*prefer_container=*/false); |
| auto window_info = apps::MakeWindowInfo(params.display_id); |
| if (params.intent) { |
| LaunchAppWithIntent( |
| params.app_id, event_flags, ConvertIntentToMojomIntent(params.intent), |
| ConvertLaunchSourceToMojomLaunchSource(params.launch_source), |
| std::move(window_info), base::DoNothing()); |
| } else { |
| Launch(params.app_id, event_flags, |
| ConvertLaunchSourceToMojomLaunchSource(params.launch_source), |
| std::move(window_info)); |
| } |
| // TODO(crbug.com/1244506): Add launch return value. |
| std::move(callback).Run(LaunchResult()); |
| } |
| |
| void ArcApps::LaunchShortcut(const std::string& app_id, |
| const std::string& shortcut_id, |
| int64_t display_id) { |
| arc::ExecuteArcShortcutCommand(profile_, app_id, shortcut_id, display_id); |
| } |
| |
| void ArcApps::OnPreferredAppSet( |
| const std::string& app_id, |
| IntentFilterPtr intent_filter, |
| IntentPtr intent, |
| ReplacedAppPreferences replaced_app_preferences) { |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| AddPreferredApp(app_id, intent_filter, std::move(intent), arc_service_manager, |
| prefs); |
| ResetVerifiedLinks(intent_filter, replaced_app_preferences, |
| arc_service_manager, prefs); |
| } |
| |
| void ArcApps::Connect( |
| mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote, |
| apps::mojom::ConnectOptionsPtr opts) { |
| std::vector<apps::mojom::AppPtr> apps; |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (prefs) { |
| for (const auto& app_id : prefs->GetAppIds()) { |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (app_info) { |
| apps.push_back(Convert(prefs, app_id, *app_info)); |
| } |
| } |
| } |
| mojo::Remote<apps::mojom::Subscriber> subscriber( |
| std::move(subscriber_remote)); |
| subscriber->OnApps(std::move(apps), apps::mojom::AppType::kArc, |
| true /* should_notify_initialized */); |
| subscribers_.Add(std::move(subscriber)); |
| } |
| |
| void ArcApps::Launch(const std::string& app_id, |
| int32_t event_flags, |
| apps::mojom::LaunchSource launch_source, |
| apps::mojom::WindowInfoPtr window_info) { |
| auto user_interaction_type = GetUserInterationType( |
| ConvertMojomLaunchSourceToLaunchSource(launch_source)); |
| if (!user_interaction_type.has_value()) { |
| return; |
| } |
| |
| if (app_id == arc::kPlayStoreAppId && |
| apps_util::IsHumanLaunch( |
| ConvertMojomLaunchSourceToLaunchSource(launch_source))) { |
| arc::RecordPlayStoreLaunchWithinAWeek(profile_->GetPrefs(), |
| /*launched=*/true); |
| } |
| |
| auto new_window_info = SetSessionId(std::move(window_info)); |
| int32_t session_id = new_window_info->window_id; |
| int64_t display_id = new_window_info->display_id; |
| |
| arc::LaunchApp(profile_, app_id, event_flags, user_interaction_type.value(), |
| MakeArcWindowInfo(std::move(new_window_info))); |
| |
| full_restore::SaveAppLaunchInfo( |
| profile_->GetPath(), std::make_unique<app_restore::AppLaunchInfo>( |
| app_id, event_flags, session_id, display_id)); |
| } |
| |
| void ArcApps::LaunchAppWithIntent(const std::string& app_id, |
| int32_t event_flags, |
| apps::mojom::IntentPtr intent, |
| apps::mojom::LaunchSource launch_source, |
| apps::mojom::WindowInfoPtr window_info, |
| LaunchAppWithIntentCallback callback) { |
| auto user_interaction_type = GetUserInterationType( |
| ConvertMojomLaunchSourceToLaunchSource(launch_source)); |
| if (!user_interaction_type.has_value()) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| if (app_id == arc::kPlayStoreAppId && |
| apps_util::IsHumanLaunch( |
| ConvertMojomLaunchSourceToLaunchSource(launch_source))) { |
| arc::RecordPlayStoreLaunchWithinAWeek(profile_->GetPrefs(), |
| /*launched=*/true); |
| } |
| |
| arc::ArcMetricsService::RecordArcUserInteraction( |
| profile_, user_interaction_type.value()); |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| LOG(ERROR) << "Launch App failed, could not find app with id " << app_id; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| if (app_info->ready) { |
| arc::mojom::ActivityNamePtr activity = arc::mojom::ActivityName::New(); |
| activity->package_name = app_info->package_name; |
| if (intent->activity_name.has_value() && |
| !intent->activity_name.value().empty()) { |
| activity->activity_name = intent->activity_name.value(); |
| } |
| |
| auto new_window_info = SetSessionId(std::move(window_info)); |
| int32_t session_id = new_window_info->window_id; |
| int64_t display_id = new_window_info->display_id; |
| |
| // Check if the intent has files, and whether the intent has a mime type or |
| // all the individual files have mime types. |
| if (IntentHasFilesAndMimeTypes(intent)) { |
| std::vector<GURL> file_urls; |
| for (const auto& file : intent->files.value()) { |
| file_urls.push_back(file->url); |
| } |
| arc::ConvertToContentUrlsAndShare( |
| profile_, apps::GetFileSystemURL(profile_, file_urls), |
| base::BindOnce(&OnContentUrlResolved, profile_->GetPath(), app_id, |
| event_flags, ConvertMojomIntentToIntent(intent), |
| std::move(activity), |
| ConvertMojomWindowInfoToWindowInfo(new_window_info), |
| std::move(callback))); |
| return; |
| } |
| |
| auto intent_for_full_restore = intent.Clone(); |
| |
| std::string intent_str = apps_util::CreateLaunchIntent( |
| app_info->package_name, ConvertMojomIntentToIntent(intent)); |
| if (!intent_str.empty()) { |
| // If |intent| can be converted to a string, call the Launch interface. |
| if (!arc::LaunchAppWithIntent( |
| profile_, app_id, intent_str, event_flags, |
| user_interaction_type.value(), |
| MakeArcWindowInfo(std::move(new_window_info)))) { |
| VLOG(2) << "Failed to launch app: " + app_id + "."; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| } else { |
| // If |intent| can't be converted to a string, call the HandleIntent |
| // interface. |
| auto arc_intent = |
| apps_util::ConvertAppServiceToArcIntent(std::move(intent)); |
| |
| if (!arc_intent) { |
| LOG(ERROR) << "Launch App failed, launch intent is not valid"; |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (!arc_service_manager) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| arc::mojom::IntentHelperInstance* instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| HandleIntentWithWindowInfo); |
| if (instance) { |
| instance->HandleIntentWithWindowInfo( |
| std::move(arc_intent), std::move(activity), |
| MakeArcWindowInfo(std::move(new_window_info))); |
| } else { |
| instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| HandleIntent); |
| if (!instance) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| |
| instance->HandleIntent(std::move(arc_intent), std::move(activity)); |
| } |
| } |
| |
| prefs->SetLastLaunchTime(app_id); |
| |
| full_restore::SaveAppLaunchInfo( |
| profile_->GetPath(), |
| std::make_unique<app_restore::AppLaunchInfo>( |
| app_id, event_flags, |
| apps::ConvertMojomIntentToIntent(intent_for_full_restore), |
| session_id, display_id)); |
| std::move(callback).Run(/*success=*/true); |
| return; |
| } |
| |
| if (arc::IsArcPlayStoreEnabledForProfile(profile_)) { |
| // Handle the case when default app tries to re-activate OptIn flow. |
| if (arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile_) && |
| !arc::ArcSessionManager::Get()->enable_requested() && |
| prefs->IsDefault(app_id)) { |
| arc::SetArcPlayStoreEnabledForProfile(profile_, true); |
| // PlayStore item has special handling for shelf controllers. In order |
| // to avoid unwanted initial animation for PlayStore item do not create |
| // deferred launch request when PlayStore item enables Google Play |
| // Store. |
| if (app_id == arc::kPlayStoreAppId) { |
| prefs->SetLastLaunchTime(app_id); |
| std::move(callback).Run(/*success=*/true); |
| return; |
| } |
| } |
| } else { |
| if (prefs->IsDefault(app_id)) { |
| // The setting can fail if the preference is managed. However, the |
| // caller is responsible to not call this function in such case. DCHECK |
| // is here to prevent possible mistake. |
| if (!arc::SetArcPlayStoreEnabledForProfile(profile_, true)) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| DCHECK(arc::IsArcPlayStoreEnabledForProfile(profile_)); |
| |
| // PlayStore item has special handling for shelf controllers. In order |
| // to avoid unwanted initial animation for PlayStore item do not create |
| // deferred launch request when PlayStore item enables Google Play |
| // Store. |
| if (app_id == arc::kPlayStoreAppId) { |
| prefs->SetLastLaunchTime(app_id); |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| } else { |
| // Only reachable when ARC always starts. |
| DCHECK(arc::ShouldArcAlwaysStart()); |
| } |
| } |
| std::move(callback).Run(/*success=*/true); |
| } |
| |
| void ArcApps::SetPermission(const std::string& app_id, |
| apps::mojom::PermissionPtr permission) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| LOG(ERROR) << "SetPermission failed, could not find app with id " << app_id; |
| return; |
| } |
| |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (!arc_service_manager) { |
| LOG(WARNING) << "SetPermission failed, ArcServiceManager not available."; |
| return; |
| } |
| |
| // TODO(crbug.com/1198390): Add unknown type for arc permissions enum. |
| arc::mojom::AppPermission permission_type = arc::mojom::AppPermission::CAMERA; |
| |
| if (!GetArcPermissionType(permission->permission_type, permission_type)) { |
| LOG(ERROR) << "SetPermission failed, permission type not supported by ARC."; |
| return; |
| } |
| |
| if (apps_util::IsPermissionEnabled(permission->value)) { |
| auto* permissions_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->app_permissions(), |
| GrantPermission); |
| if (permissions_instance) { |
| permissions_instance->GrantPermission(app_info->package_name, |
| permission_type); |
| } |
| } else { |
| auto* permissions_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->app_permissions(), |
| RevokePermission); |
| if (permissions_instance) { |
| permissions_instance->RevokePermission(app_info->package_name, |
| permission_type); |
| } |
| } |
| } |
| |
| void ArcApps::SetResizeLocked(const std::string& app_id, |
| apps::mojom::OptionalBool locked) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| if (locked == apps::mojom::OptionalBool::kUnknown) { |
| return; |
| } |
| prefs->SetResizeLockState(app_id, locked == apps::mojom::OptionalBool::kTrue |
| ? arc::mojom::ArcResizeLockState::ON |
| : arc::mojom::ArcResizeLockState::OFF); |
| } |
| |
| void ArcApps::Uninstall(const std::string& app_id, |
| apps::mojom::UninstallSource uninstall_source, |
| bool clear_site_data, |
| bool report_abuse) { |
| arc::UninstallArcApp(app_id, profile_); |
| } |
| |
| void ArcApps::PauseApp(const std::string& app_id) { |
| if (paused_apps_.MaybeAddApp(app_id)) { |
| SetIconEffect(app_id); |
| } |
| |
| constexpr bool kPaused = true; |
| PublisherBase::Publish(paused_apps_.GetAppWithPauseStatus( |
| apps::mojom::AppType::kArc, app_id, kPaused), |
| subscribers_); |
| AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus( |
| AppType::kArc, app_id, /*paused=*/true)); |
| CloseTasks(app_id); |
| } |
| |
| void ArcApps::UnpauseApp(const std::string& app_id) { |
| if (paused_apps_.MaybeRemoveApp(app_id)) { |
| SetIconEffect(app_id); |
| } |
| |
| constexpr bool kPaused = false; |
| PublisherBase::Publish(paused_apps_.GetAppWithPauseStatus( |
| apps::mojom::AppType::kArc, app_id, kPaused), |
| subscribers_); |
| AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus( |
| AppType::kArc, app_id, /*paused=*/false)); |
| } |
| |
| void ArcApps::StopApp(const std::string& app_id) { |
| CloseTasks(app_id); |
| } |
| |
| void ArcApps::GetMenuModel(const std::string& app_id, |
| apps::mojom::MenuType menu_type, |
| int64_t display_id, |
| GetMenuModelCallback callback) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| std::move(callback).Run(apps::mojom::MenuItems::New()); |
| return; |
| } |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| std::move(callback).Run(apps::mojom::MenuItems::New()); |
| return; |
| } |
| |
| apps::mojom::MenuItemsPtr menu_items = apps::mojom::MenuItems::New(); |
| |
| // Add Open item if the app is not opened and not suspended. |
| if (!base::Contains(app_id_to_task_ids_, app_id) && !app_info->suspended) { |
| AddCommandItem(ash::LAUNCH_NEW, IDS_APP_CONTEXT_MENU_ACTIVATE_ARC, |
| &menu_items); |
| } |
| |
| if (app_info->shortcut) { |
| AddCommandItem(ash::UNINSTALL, IDS_APP_LIST_REMOVE_SHORTCUT, &menu_items); |
| } else if (app_info->ready && !app_info->sticky) { |
| AddCommandItem(ash::UNINSTALL, IDS_APP_LIST_UNINSTALL_ITEM, &menu_items); |
| } |
| |
| // App Info item. |
| if (app_info->ready && ShouldShow(*app_info)) { |
| AddCommandItem(ash::SHOW_APP_INFO, IDS_APP_CONTEXT_MENU_SHOW_INFO, |
| &menu_items); |
| } |
| |
| if (menu_type == apps::mojom::MenuType::kShelf && |
| base::Contains(app_id_to_task_ids_, app_id)) { |
| AddCommandItem(ash::MENU_CLOSE, IDS_SHELF_CONTEXT_MENU_CLOSE, &menu_items); |
| } |
| |
| BuildMenuForShortcut(app_info->package_name, std::move(menu_items), |
| std::move(callback)); |
| } |
| |
| void ArcApps::ExecuteContextMenuCommand(const std::string& app_id, |
| int command_id, |
| const std::string& shortcut_id, |
| int64_t display_id) { |
| arc::ExecuteArcShortcutCommand(profile_, app_id, shortcut_id, display_id); |
| } |
| |
| void ArcApps::OpenNativeSettings(const std::string& app_id) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| LOG(ERROR) << "Cannot open native settings for " << app_id |
| << ". App is not found."; |
| return; |
| } |
| arc::ShowPackageInfo(app_info->package_name, |
| arc::mojom::ShowPackageInfoPage::MAIN, |
| display::Screen::GetScreen()->GetPrimaryDisplay().id()); |
| } |
| |
| void ArcApps::OnPreferredAppSet( |
| const std::string& app_id, |
| apps::mojom::IntentFilterPtr intent_filter, |
| apps::mojom::IntentPtr intent, |
| apps::mojom::ReplacedAppPreferencesPtr replaced_app_preferences) { |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| AddPreferredApp(app_id, ConvertMojomIntentFilterToIntentFilter(intent_filter), |
| ConvertMojomIntentToIntent(intent), arc_service_manager, |
| prefs); |
| ResetVerifiedLinks(ConvertMojomIntentFilterToIntentFilter(intent_filter), |
| ConvertMojomReplacedAppPreferencesToReplacedAppPreferences( |
| replaced_app_preferences), |
| arc_service_manager, prefs); |
| } |
| |
| void ArcApps::OnSupportedLinksPreferenceChanged(const std::string& app_id, |
| bool open_in_app) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (!app_info) { |
| return; |
| } |
| |
| arc::mojom::IntentHelperInstance* instance = nullptr; |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| if (arc_service_manager) { |
| instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_service_manager->arc_bridge_service()->intent_helper(), |
| SetVerifiedLinks); |
| } |
| if (!instance) { |
| return; |
| } |
| |
| instance->SetVerifiedLinks({app_info->package_name}, open_in_app); |
| } |
| |
| void ArcApps::OnAppRegistered(const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (prefs && !IsWebAppShellPackage(profile_, app_info)) { |
| PublisherBase::Publish(Convert(prefs, app_id, app_info), subscribers_); |
| AppPublisher::Publish(CreateApp(prefs, app_id, app_info)); |
| } |
| } |
| |
| void ArcApps::OnAppStatesChanged(const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs || IsWebAppShellPackage(profile_, app_info)) { |
| return; |
| } |
| |
| PublisherBase::Publish(Convert(prefs, app_id, app_info), subscribers_); |
| AppPublisher::Publish(CreateApp(prefs, app_id, app_info)); |
| } |
| |
| void ArcApps::OnAppRemoved(const std::string& app_id) { |
| app_notifications_.RemoveNotificationsForApp(app_id); |
| paused_apps_.MaybeRemoveApp(app_id); |
| |
| if (base::Contains(app_id_to_task_ids_, app_id)) { |
| for (int task_id : app_id_to_task_ids_[app_id]) { |
| task_id_to_app_id_.erase(task_id); |
| } |
| app_id_to_task_ids_.erase(app_id); |
| } |
| |
| apps::mojom::AppPtr mojom_app = apps::mojom::App::New(); |
| mojom_app->app_type = apps::mojom::AppType::kArc; |
| mojom_app->app_id = app_id; |
| mojom_app->readiness = apps::mojom::Readiness::kUninstalledByUser; |
| PublisherBase::Publish(std::move(mojom_app), subscribers_); |
| |
| auto app = std::make_unique<App>(AppType::kArc, app_id); |
| app->readiness = Readiness::kUninstalledByUser; |
| AppPublisher::Publish(std::move(app)); |
| } |
| |
| void ArcApps::OnAppIconUpdated(const std::string& app_id, |
| const ArcAppIconDescriptor& descriptor) { |
| SetIconEffect(app_id); |
| } |
| |
| void ArcApps::OnAppNameUpdated(const std::string& app_id, |
| const std::string& name) { |
| apps::mojom::AppPtr mojom_app = apps::mojom::App::New(); |
| mojom_app->app_type = apps::mojom::AppType::kArc; |
| mojom_app->app_id = app_id; |
| mojom_app->name = name; |
| PublisherBase::Publish(std::move(mojom_app), subscribers_); |
| |
| auto app = std::make_unique<App>(AppType::kArc, app_id); |
| app->name = name; |
| AppPublisher::Publish(std::move(app)); |
| } |
| |
| void ArcApps::OnAppLastLaunchTimeUpdated(const std::string& app_id) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (!app_info || IsWebAppShellPackage(profile_, *app_info)) { |
| return; |
| } |
| apps::mojom::AppPtr mojom_app = apps::mojom::App::New(); |
| mojom_app->app_type = apps::mojom::AppType::kArc; |
| mojom_app->app_id = app_id; |
| mojom_app->last_launch_time = app_info->last_launch_time; |
| PublisherBase::Publish(std::move(mojom_app), subscribers_); |
| |
| auto app = std::make_unique<App>(AppType::kArc, app_id); |
| app->last_launch_time = app_info->last_launch_time; |
| AppPublisher::Publish(std::move(app)); |
| } |
| |
| void ArcApps::OnPackageInstalled( |
| const arc::mojom::ArcPackageInfo& package_info) { |
| ConvertAndPublishPackageApps(package_info); |
| } |
| |
| void ArcApps::OnPackageModified( |
| const arc::mojom::ArcPackageInfo& package_info) { |
| static constexpr bool update_icon = false; |
| ConvertAndPublishPackageApps(package_info, update_icon); |
| } |
| |
| void ArcApps::OnPackageListInitialRefreshed() { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| // This method is called when ARC++ finishes booting. Do not update the icon; |
| // it should be impossible for the icon to have changed since ARC++ has not |
| // been running. |
| static constexpr bool update_icon = false; |
| for (const auto& app_id : prefs->GetAppIds()) { |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (app_info && !IsWebAppShellPackage(profile_, *app_info)) { |
| PublisherBase::Publish(Convert(prefs, app_id, *app_info, update_icon), |
| subscribers_); |
| AppPublisher::Publish(CreateApp(prefs, app_id, *app_info, update_icon)); |
| } |
| } |
| } |
| |
| void ArcApps::OnTaskCreated(int32_t task_id, |
| const std::string& package_name, |
| const std::string& activity, |
| const std::string& intent, |
| int32_t session_id) { |
| const std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity); |
| app_id_to_task_ids_[app_id].insert(task_id); |
| task_id_to_app_id_[task_id] = app_id; |
| } |
| |
| void ArcApps::OnTaskDestroyed(int32_t task_id) { |
| auto it = task_id_to_app_id_.find(task_id); |
| if (it == task_id_to_app_id_.end()) { |
| return; |
| } |
| |
| const std::string app_id = it->second; |
| task_id_to_app_id_.erase(it); |
| DCHECK(base::Contains(app_id_to_task_ids_, app_id)); |
| app_id_to_task_ids_[app_id].erase(task_id); |
| if (app_id_to_task_ids_[app_id].empty()) { |
| app_id_to_task_ids_.erase(app_id); |
| } |
| } |
| |
| void ArcApps::OnIntentFiltersUpdated( |
| const absl::optional<std::string>& package_name) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| |
| auto GetAppInfoAndPublish = [prefs, this](std::string app_id) { |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (app_info) { |
| PublisherBase::Publish( |
| Convert(prefs, app_id, *app_info, false /* update_icon */), |
| subscribers_); |
| AppPublisher::Publish( |
| CreateApp(prefs, app_id, *app_info, false /* update_icon */)); |
| } |
| }; |
| |
| // If there is no specific package_name, update all apps, otherwise update |
| // apps for the package. |
| |
| // Note: Cannot combine the two for-loops because the return type of |
| // GetAppIds() is std::vector<std::string> and the return type of |
| // GetAppsForPackage() is std::unordered_set<std::string>. |
| if (package_name == absl::nullopt) { |
| for (const auto& app_id : prefs->GetAppIds()) { |
| GetAppInfoAndPublish(app_id); |
| } |
| } else { |
| for (const auto& app_id : prefs->GetAppsForPackage(package_name.value())) { |
| GetAppInfoAndPublish(app_id); |
| } |
| } |
| } |
| |
| // This method calls App Service directly with a list of intent filters received |
| // from ARC, rather than going through AppServiceProxy's |
| // SetSupportedlinksPreference method. This is because recent changes to app |
| // intent filters (such as after an install or update) may not have propagated |
| // to AppServiceProxy's AppRegistryCache yet. |
| // TODO(crbug.com/1265315): Use AppServiceProxy to set the preference once app |
| // changes are synchronous within Ash. |
| void ArcApps::OnArcSupportedLinksChanged( |
| const std::vector<arc::mojom::SupportedLinksPtr>& added, |
| const std::vector<arc::mojom::SupportedLinksPtr>& removed, |
| arc::mojom::SupportedLinkChangeSource source) { |
| mojo::Remote<apps::mojom::AppService>& app_service = proxy()->AppService(); |
| if (!app_service.is_bound()) { |
| return; |
| } |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| |
| for (const auto& supported_link : added) { |
| std::string app_id = |
| prefs->GetAppIdByPackageName(supported_link->package_name); |
| if (app_id.empty() || !supported_link->filters.has_value()) { |
| continue; |
| } |
| |
| // When kDefaultLinkCapturingInBrowser is enabled, ignore any requests from |
| // ARC to set an app as handling supported links by default. We allow |
| // requests if they were initiated by user action, or if the app already |
| // has a non-default setting on the browser side. |
| bool should_ignore_update = |
| base::FeatureList::GetInstance()->IsEnabled( |
| features::kDefaultLinkCapturingInBrowser) && |
| source == arc::mojom::SupportedLinkChangeSource::kArcSystem && |
| !proxy()->PreferredAppsList().IsPreferredAppForSupportedLinks(app_id); |
| |
| if (should_ignore_update) { |
| continue; |
| } |
| |
| apps::IntentFilters app_service_filters; |
| for (const auto& arc_filter : supported_link->filters.value()) { |
| auto converted = apps_util::CreateIntentFilterForArc(arc_filter); |
| if (apps_util::IsSupportedLinkForApp(app_id, converted)) { |
| app_service_filters.push_back(std::move(converted)); |
| } |
| } |
| |
| proxy()->SetSupportedLinksPreference(app_id, |
| std::move(app_service_filters)); |
| } |
| |
| for (const auto& supported_link : removed) { |
| std::string app_id = |
| prefs->GetAppIdByPackageName(supported_link->package_name); |
| if (app_id.empty()) { |
| continue; |
| } |
| proxy()->RemoveSupportedLinksPreference(app_id); |
| } |
| } |
| |
| void ArcApps::OnSetArcNotificationsInstance( |
| ash::ArcNotificationManagerBase* arc_notification_manager) { |
| DCHECK(arc_notification_manager); |
| notification_observation_.Observe(arc_notification_manager); |
| } |
| |
| void ArcApps::OnArcNotificationInitializerDestroyed( |
| ash::ArcNotificationsHostInitializer* initializer) { |
| DCHECK(notification_initializer_observation_.IsObservingSource(initializer)); |
| notification_initializer_observation_.Reset(); |
| } |
| |
| void ArcApps::OnNotificationUpdated(const std::string& notification_id, |
| const std::string& app_id) { |
| if (app_id.empty()) { |
| return; |
| } |
| |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| |
| const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (!app_info) { |
| return; |
| } |
| |
| app_notifications_.AddNotification(app_id, notification_id); |
| PublisherBase::Publish(app_notifications_.GetAppWithHasBadgeStatus( |
| apps::mojom::AppType::kArc, app_id), |
| subscribers_); |
| AppPublisher::Publish( |
| app_notifications_.CreateAppWithHasBadgeStatus(AppType::kArc, app_id)); |
| } |
| |
| void ArcApps::OnNotificationRemoved(const std::string& notification_id) { |
| const auto app_ids = |
| app_notifications_.GetAppIdsForNotification(notification_id); |
| if (app_ids.empty()) { |
| return; |
| } |
| |
| app_notifications_.RemoveNotification(notification_id); |
| |
| for (const auto& app_id : app_ids) { |
| PublisherBase::Publish(app_notifications_.GetAppWithHasBadgeStatus( |
| apps::mojom::AppType::kArc, app_id), |
| subscribers_); |
| AppPublisher::Publish( |
| app_notifications_.CreateAppWithHasBadgeStatus(AppType::kArc, app_id)); |
| } |
| } |
| |
| void ArcApps::OnArcNotificationManagerDestroyed( |
| ash::ArcNotificationManagerBase* notification_manager) { |
| DCHECK(notification_observation_.IsObservingSource(notification_manager)); |
| notification_observation_.Reset(); |
| } |
| |
| void ArcApps::OnPrivacyItemsChanged( |
| std::vector<arc::mojom::PrivacyItemPtr> privacy_items) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| |
| // Get the existing accessing app ids from `accessing_apps_`, and set all of |
| // them as false to explicitly update `AppCapabilityAccessCache` to ensure the |
| // access is stopped when they are not list in `privacy_items`. If they are |
| // still accessing, they will exist in `privacy_items`, and be set as true in |
| // the next loop for `privacy_items`. |
| base::flat_map<std::string, apps::mojom::CapabilityAccessPtr> |
| capability_accesses; |
| for (const auto& app_id : accessing_apps_) { |
| auto access = apps::mojom::CapabilityAccess::New(); |
| access->app_id = app_id; |
| access->camera = apps::mojom::OptionalBool::kFalse; |
| access->microphone = apps::mojom::OptionalBool::kFalse; |
| capability_accesses[app_id] = std::move(access); |
| } |
| accessing_apps_.clear(); |
| |
| // Check the new items in `privacy_items`, and update `capability_accesses` to |
| // set the access item as true, if the camera or the microphone is still in |
| // use. |
| for (const auto& item : privacy_items) { |
| arc::mojom::AppPermissionGroup permission = item->permission_group; |
| if (permission != arc::mojom::AppPermissionGroup::CAMERA && |
| permission != arc::mojom::AppPermissionGroup::MICROPHONE) { |
| continue; |
| } |
| |
| auto package_name = item->privacy_application->package_name; |
| for (const auto& app_id : prefs->GetAppsForPackage(package_name)) { |
| accessing_apps_.insert(app_id); |
| auto it = capability_accesses.find(app_id); |
| if (it == capability_accesses.end()) { |
| capability_accesses[app_id] = apps::mojom::CapabilityAccess::New(); |
| it = capability_accesses.find(app_id); |
| it->second->app_id = app_id; |
| } |
| if (permission == arc::mojom::AppPermissionGroup::CAMERA) { |
| it->second->camera = apps::mojom::OptionalBool::kTrue; |
| } |
| if (permission == arc::mojom::AppPermissionGroup::MICROPHONE) { |
| it->second->microphone = apps::mojom::OptionalBool::kTrue; |
| } |
| } |
| } |
| |
| // Write the record to `AppCapabilityAccessCache`. |
| for (auto& subscriber : subscribers_) { |
| std::vector<apps::mojom::CapabilityAccessPtr> accesses; |
| for (const auto& item : capability_accesses) { |
| accesses.push_back(item.second->Clone()); |
| } |
| subscriber->OnCapabilityAccesses(std::move(accesses)); |
| } |
| } |
| |
| void ArcApps::OnInstanceUpdate(const apps::InstanceUpdate& update) { |
| if (!update.StateChanged()) { |
| return; |
| } |
| if (update.AppId() != arc::kSettingsAppId) { |
| return; |
| } |
| if (update.State() & apps::InstanceState::kActive) { |
| settings_app_is_active_ = true; |
| } else if (settings_app_is_active_) { |
| settings_app_is_active_ = false; |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| RequestDomainVerificationStatusUpdate(prefs); |
| } |
| } |
| |
| void ArcApps::OnInstanceRegistryWillBeDestroyed( |
| apps::InstanceRegistry* instance_registry) { |
| DCHECK(instance_registry_observation_.IsObservingSource(instance_registry)); |
| instance_registry_observation_.Reset(); |
| } |
| |
| void ArcApps::LoadPlayStoreIcon(apps::IconType icon_type, |
| int32_t size_hint_in_dip, |
| IconEffects icon_effects, |
| apps::LoadIconCallback callback) { |
| // Use overloaded Chrome icon for Play Store that is adapted to Chrome style. |
| constexpr bool quantize_to_supported_scale_factor = true; |
| int size_hint_in_px = apps_util::ConvertDipToPx( |
| size_hint_in_dip, quantize_to_supported_scale_factor); |
| int resource_id = (size_hint_in_px <= 32) ? IDR_ARC_SUPPORT_ICON_32 |
| : IDR_ARC_SUPPORT_ICON_192; |
| constexpr bool is_placeholder_icon = false; |
| LoadIconFromResource(icon_type, size_hint_in_dip, resource_id, |
| is_placeholder_icon, icon_effects, std::move(callback)); |
| } |
| |
| apps::mojom::InstallReason GetInstallReason( |
| const ArcAppListPrefs* prefs, |
| const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info) { |
| // Sticky represents apps that cannot be uninstalled and are installed by the |
| // system. |
| if (app_info.sticky) { |
| return apps::mojom::InstallReason::kSystem; |
| } |
| |
| if (prefs->IsOem(app_id)) { |
| return apps::mojom::InstallReason::kOem; |
| } |
| |
| if (prefs->IsDefault(app_id)) { |
| return apps::mojom::InstallReason::kDefault; |
| } |
| |
| if (prefs->IsControlledByPolicy(app_info.package_name)) { |
| return apps::mojom::InstallReason::kPolicy; |
| } |
| |
| return apps::mojom::InstallReason::kUser; |
| } |
| |
| AppPtr ArcApps::CreateApp(ArcAppListPrefs* prefs, |
| const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info, |
| bool update_icon) { |
| auto install_reason = ConvertMojomInstallReasonToInstallReason( |
| GetInstallReason(prefs, app_id, app_info)); |
| auto app = AppPublisher::MakeApp( |
| AppType::kArc, app_id, |
| app_info.suspended ? Readiness::kDisabledByPolicy : Readiness::kReady, |
| app_info.name, install_reason, |
| install_reason == InstallReason::kSystem ? InstallSource::kSystem |
| : InstallSource::kPlayStore); |
| |
| app->publisher_id = app_info.package_name; |
| |
| if (update_icon) { |
| app->icon_key = std::move( |
| *icon_key_factory_.CreateIconKey(GetIconEffects(app_id, app_info))); |
| } |
| |
| app->version = app_info.version_name; |
| |
| app->last_launch_time = app_info.last_launch_time; |
| app->install_time = app_info.install_time; |
| |
| std::unique_ptr<ArcAppListPrefs::PackageInfo> package = |
| prefs->GetPackage(app_info.package_name); |
| if (package) { |
| app->permissions = CreatePermissions(package->permissions); |
| } |
| |
| auto show = ShouldShow(app_info); |
| |
| // All published ARC apps are launchable. All launchable apps should be |
| // permitted to be shown on the shelf, and have their pins on the shelf |
| // persisted. |
| app->show_in_shelf = true; |
| app->show_in_launcher = show; |
| |
| if (app_id == arc::kPlayGamesAppId && !show) { |
| // Play Games should only be hidden in the launcher. |
| app->show_in_search = true; |
| app->show_in_management = true; |
| } else { |
| app->show_in_search = show; |
| app->show_in_management = show; |
| } |
| |
| app->handles_intents = show; |
| |
| app->allow_uninstall = app_info.ready && !app_info.sticky; |
| |
| app->has_badge = app_notifications_.HasNotification(app_id); |
| app->paused = paused_apps_.IsPaused(app_id); |
| |
| auto* intent_helper_bridge = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper_bridge && |
| app_info.package_name != arc::kArcIntentHelperPackageName) { |
| app->intent_filters = apps_util::CreateIntentFiltersFromArcBridge( |
| app_info.package_name, intent_helper_bridge); |
| } |
| |
| app->resize_locked = GetResizeLocked(prefs, app_id); |
| |
| app->app_size_in_bytes = app_info.app_size_in_bytes; |
| app->data_size_in_bytes = app_info.data_size_in_bytes; |
| |
| // TODO(crbug.com/1253250): Add other fields for the App struct. |
| return app; |
| } |
| |
| apps::mojom::AppPtr ArcApps::Convert(ArcAppListPrefs* prefs, |
| const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info, |
| bool update_icon) { |
| auto install_reason = GetInstallReason(prefs, app_id, app_info); |
| apps::mojom::AppPtr app = PublisherBase::MakeApp( |
| apps::mojom::AppType::kArc, app_id, |
| app_info.suspended ? apps::mojom::Readiness::kDisabledByPolicy |
| : apps::mojom::Readiness::kReady, |
| app_info.name, install_reason); |
| |
| app->publisher_id = app_info.package_name; |
| |
| auto paused = paused_apps_.IsPaused(app_id) |
| ? apps::mojom::OptionalBool::kTrue |
| : apps::mojom::OptionalBool::kFalse; |
| |
| if (update_icon) { |
| app->icon_key = |
| icon_key_factory_.MakeIconKey(GetIconEffects(app_id, app_info)); |
| } |
| |
| app->version = app_info.version_name; |
| |
| app->last_launch_time = app_info.last_launch_time; |
| app->install_time = app_info.install_time; |
| app->install_source = install_reason == apps::mojom::InstallReason::kSystem |
| ? apps::mojom::InstallSource::kSystem |
| : apps::mojom::InstallSource::kPlayStore; |
| |
| auto show = ShouldShow(app_info) ? apps::mojom::OptionalBool::kTrue |
| : apps::mojom::OptionalBool::kFalse; |
| // All published ARC apps are launchable. All launchable apps should be |
| // permitted to be shown on the shelf, and have their pins on the shelf |
| // persisted. |
| app->show_in_shelf = apps::mojom::OptionalBool::kTrue; |
| app->show_in_launcher = show; |
| app->handles_intents = show; |
| |
| if (app_id == arc::kPlayGamesAppId && |
| show == apps::mojom::OptionalBool::kFalse) { |
| // Play Games should only be hidden in the launcher. |
| app->show_in_search = apps::mojom::OptionalBool::kTrue; |
| app->show_in_management = apps::mojom::OptionalBool::kTrue; |
| } else { |
| app->show_in_search = show; |
| app->show_in_management = show; |
| } |
| |
| app->allow_uninstall = (app_info.ready && !app_info.sticky) |
| ? apps::mojom::OptionalBool::kTrue |
| : apps::mojom::OptionalBool::kFalse; |
| |
| app->has_badge = app_notifications_.HasNotification(app_id) |
| ? apps::mojom::OptionalBool::kTrue |
| : apps::mojom::OptionalBool::kFalse; |
| app->paused = paused; |
| app->resize_locked = IsResizeLocked(prefs, app_id); |
| |
| std::unique_ptr<ArcAppListPrefs::PackageInfo> package = |
| prefs->GetPackage(app_info.package_name); |
| if (package) { |
| UpdateAppPermissions(package->permissions, &app->permissions); |
| } |
| |
| auto* intent_helper_bridge = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper_bridge && |
| app_info.package_name != arc::kArcIntentHelperPackageName) { |
| UpdateAppIntentFilters(app_info.package_name, intent_helper_bridge, |
| &app->intent_filters); |
| } |
| |
| return app; |
| } |
| |
| void ArcApps::ConvertAndPublishPackageApps( |
| const arc::mojom::ArcPackageInfo& package_info, |
| bool update_icon) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (prefs) { |
| for (const auto& app_id : |
| prefs->GetAppsForPackage(package_info.package_name)) { |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| prefs->GetApp(app_id); |
| if (app_info && !IsWebAppShellPackage(profile_, *app_info)) { |
| PublisherBase::Publish(Convert(prefs, app_id, *app_info, update_icon), |
| subscribers_); |
| AppPublisher::Publish(CreateApp(prefs, app_id, *app_info, update_icon)); |
| } |
| } |
| } |
| } |
| |
| IconEffects ArcApps::GetIconEffects(const std::string& app_id, |
| const ArcAppListPrefs::AppInfo& app_info) { |
| IconEffects icon_effects = IconEffects::kNone; |
| if (app_info.suspended) { |
| icon_effects = |
| static_cast<IconEffects>(icon_effects | IconEffects::kBlocked); |
| } |
| if (paused_apps_.IsPaused(app_id)) { |
| icon_effects = |
| static_cast<IconEffects>(icon_effects | IconEffects::kPaused); |
| } |
| return icon_effects; |
| } |
| |
| void ArcApps::SetIconEffect(const std::string& app_id) { |
| ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_); |
| if (!prefs) { |
| return; |
| } |
| std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| if (!app_info || IsWebAppShellPackage(profile_, *app_info)) { |
| return; |
| } |
| |
| apps::mojom::AppPtr mojom_app = apps::mojom::App::New(); |
| mojom_app->app_type = apps::mojom::AppType::kArc; |
| mojom_app->app_id = app_id; |
| mojom_app->icon_key = |
| icon_key_factory_.MakeIconKey(GetIconEffects(app_id, *app_info)); |
| PublisherBase::Publish(std::move(mojom_app), subscribers_); |
| |
| auto app = std::make_unique<App>(AppType::kArc, app_id); |
| app->icon_key = std::move( |
| *icon_key_factory_.CreateIconKey(GetIconEffects(app_id, *app_info))); |
| AppPublisher::Publish(std::move(app)); |
| } |
| |
| void ArcApps::CloseTasks(const std::string& app_id) { |
| if (!base::Contains(app_id_to_task_ids_, app_id)) { |
| return; |
| } |
| |
| for (int task_id : app_id_to_task_ids_[app_id]) { |
| arc::CloseTask(task_id); |
| task_id_to_app_id_.erase(task_id); |
| } |
| app_id_to_task_ids_.erase(app_id); |
| } |
| |
| void ArcApps::UpdateAppIntentFilters( |
| std::string package_name, |
| arc::ArcIntentHelperBridge* intent_helper_bridge, |
| std::vector<apps::mojom::IntentFilterPtr>* intent_filters) { |
| const std::vector<arc::IntentFilter>& arc_intent_filters = |
| intent_helper_bridge->GetIntentFilterForPackage(package_name); |
| for (auto& arc_intent_filter : arc_intent_filters) { |
| intent_filters->push_back( |
| apps_util::ConvertArcToAppServiceIntentFilter(arc_intent_filter)); |
| } |
| } |
| |
| void ArcApps::BuildMenuForShortcut(const std::string& package_name, |
| apps::mojom::MenuItemsPtr menu_items, |
| GetMenuModelCallback callback) { |
| // The previous request is cancelled, and start a new request if the callback |
| // of the previous request is not called. |
| arc_app_shortcuts_request_ = |
| std::make_unique<arc::ArcAppShortcutsRequest>(base::BindOnce( |
| &ArcApps::OnGetAppShortcutItems, weak_ptr_factory_.GetWeakPtr(), |
| base::TimeTicks::Now(), std::move(menu_items), std::move(callback))); |
| arc_app_shortcuts_request_->StartForPackage(package_name); |
| } |
| |
| void ArcApps::OnGetAppShortcutItems( |
| const base::TimeTicks start_time, |
| apps::mojom::MenuItemsPtr menu_items, |
| GetMenuModelCallback callback, |
| std::unique_ptr<apps::AppShortcutItems> app_shortcut_items) { |
| if (!app_shortcut_items || app_shortcut_items->empty()) { |
| // No need log time for empty requests. |
| std::move(callback).Run(std::move(menu_items)); |
| arc_app_shortcuts_request_.reset(); |
| return; |
| } |
| |
| apps::AppShortcutItems& items = *app_shortcut_items; |
| // Sort the shortcuts based on two rules: (1) Static (declared in manifest) |
| // shortcuts and then dynamic shortcuts; (2) Within each shortcut type |
| // (static and dynamic), shortcuts are sorted in order of increasing rank. |
| std::sort(items.begin(), items.end(), |
| [](const apps::AppShortcutItem& item1, |
| const apps::AppShortcutItem& item2) { |
| return std::tie(item1.type, item1.rank) < |
| std::tie(item2.type, item2.rank); |
| }); |
| |
| AddSeparator(ui::DOUBLE_SEPARATOR, &menu_items); |
| int command_id = ash::LAUNCH_APP_SHORTCUT_FIRST; |
| for (const auto& item : items) { |
| if (command_id != ash::LAUNCH_APP_SHORTCUT_FIRST) { |
| AddSeparator(ui::PADDED_SEPARATOR, &menu_items); |
| } |
| AddShortcutCommandItem(command_id++, item.shortcut_id, item.short_label, |
| item.icon, &menu_items); |
| } |
| std::move(callback).Run(std::move(menu_items)); |
| arc_app_shortcuts_request_.reset(); |
| |
| UMA_HISTOGRAM_TIMES("Arc.AppShortcuts.BuildMenuTime", |
| base::TimeTicks::Now() - start_time); |
| } |
| |
| } // namespace apps |