Skip to content

Commit

Permalink
Update AppCapabilityAccessCache when Arc apps access Camera or Microp…
Browse files Browse the repository at this point in the history
…hone.

Modify ArcApps to observe arc::mojom::PrivacyItemsHost for the camera or
microphone accessing.

Implement OnPrivacyItemsChanged to update the camera or microphone
accessing status in AppCapabilityAccessCache. For example:
1. OnPrivacyItemsChanged(App1:Camera) = App1 is using Camera
2. OnPrivacyItemsChanged(App1:Camera, App2:Microphone) =
     App1 is using Camera &
     App2 is using Microphone
3. OnPrivacyItemsChanged(App1:Camera) =
     App1 is using Camera
     App2 stops using Microphone
4. OnPrivacyItemsChanged(App1, Microphone) =
     App1 stops using Camera
     App1 is using Microphone
5. OnPrivacyItemsChanged() = App1 stops using Microphone

BUG=1299988

Change-Id: I08d1cc64b2877b0bfb3b61ad4edeedbbcf763bf6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3538149
Reviewed-by: Dominick Ng <dominickn@chromium.org>
Commit-Queue: Nancy Wang <nancylingwang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#985577}
  • Loading branch information
Nancy Wang authored and Chromium LUCI CQ committed Mar 26, 2022
1 parent f2801dc commit e6b7511
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 1 deletion.
61 changes: 61 additions & 0 deletions chrome/browser/apps/app_service/publishers/arc_apps.cc
Expand Up @@ -1474,6 +1474,67 @@ void ArcApps::OnArcNotificationManagerDestroyed(
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;
Expand Down
16 changes: 15 additions & 1 deletion chrome/browser/apps/app_service/publishers/arc_apps.h
Expand Up @@ -11,10 +11,13 @@
#include <string>
#include <vector>

#include "ash/components/arc/mojom/app_permissions.mojom.h"
#include "ash/components/arc/mojom/intent_helper.mojom-forward.h"
#include "ash/components/arc/mojom/privacy_items.mojom.h"
#include "ash/public/cpp/message_center/arc_notification_manager_base.h"
#include "ash/public/cpp/message_center/arc_notifications_host_initializer.h"
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
Expand Down Expand Up @@ -63,7 +66,8 @@ class ArcApps : public KeyedService,
public arc::ArcIntentHelperObserver,
public ash::ArcNotificationManagerBase::Observer,
public ash::ArcNotificationsHostInitializer::Observer,
public apps::InstanceRegistry::Observer {
public apps::InstanceRegistry::Observer,
public arc::mojom::PrivacyItemsHost {
public:
static ArcApps* Get(Profile* profile);

Expand All @@ -83,6 +87,7 @@ class ArcApps : public KeyedService,
friend class ArcAppsFactory;
friend class PublisherTest;
FRIEND_TEST_ALL_PREFIXES(PublisherTest, ArcAppsOnApps);
FRIEND_TEST_ALL_PREFIXES(PublisherTest, ArcApps_CapabilityAccess);

using AppIdToTaskIds = std::map<std::string, std::set<int>>;
using TaskIdToAppId = std::map<int, std::string>;
Expand Down Expand Up @@ -196,6 +201,12 @@ class ArcApps : public KeyedService,
void OnArcNotificationManagerDestroyed(
ash::ArcNotificationManagerBase* notification_manager) override;

// PrivacyItemsHost overrides.
void OnPrivacyItemsChanged(
std::vector<arc::mojom::PrivacyItemPtr> privacy_items) override;
void OnMicCameraIndicatorRequirementChanged(bool flag) override {}
void OnLocationIndicatorRequirementChanged(bool flag) override {}

// apps::InstanceRegistry::Observer overrides.
void OnInstanceUpdate(const apps::InstanceUpdate& update) override;
void OnInstanceRegistryWillBeDestroyed(
Expand Down Expand Up @@ -250,6 +261,9 @@ class ArcApps : public KeyedService,
AppIdToTaskIds app_id_to_task_ids_;
TaskIdToAppId task_id_to_app_id_;

// App id set which might be accessing camera or microphone.
base::flat_set<std::string> accessing_apps_;

// Handles requesting app shortcuts from Android.
std::unique_ptr<arc::ArcAppShortcutsRequest> arc_app_shortcuts_request_;

Expand Down
132 changes: 132 additions & 0 deletions chrome/browser/apps/app_service/publishers/publisher_unittest.cc
Expand Up @@ -52,6 +52,8 @@
#include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
#include "chrome/common/chrome_features.h"
#include "chromeos/login/login_state/login_state.h"
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
#include "components/services/app_service/public/cpp/capability_access_update.h"
#include "components/user_manager/scoped_user_manager.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)

Expand Down Expand Up @@ -181,6 +183,18 @@ MATCHER_P(ShownInLauncher, shown, "App shown in the launcher") {
return arg.show_in_launcher.has_value() && arg.show_in_launcher == shown;
}

#if BUILDFLAG(IS_CHROMEOS_ASH)
arc::mojom::PrivacyItemPtr CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup permission,
const std::string& package_name) {
arc::mojom::PrivacyItemPtr item = arc::mojom::PrivacyItem::New();
item->permission_group = permission;
item->privacy_application = arc::mojom::PrivacyApplication::New();
item->privacy_application->package_name = package_name;
return item;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)

} // namespace

namespace apps {
Expand Down Expand Up @@ -364,6 +378,22 @@ class PublisherTest : public extensions::ExtensionServiceTestBase {
ASSERT_TRUE(base::Contains(cache.InitializedAppTypes(), app_type));
}

void VerifyCapabilityAccess(const std::string& app_id,
apps::mojom::OptionalBool accessing_camera,
apps::mojom::OptionalBool accessing_microphone) {
apps::mojom::OptionalBool camera = apps::mojom::OptionalBool::kUnknown;
apps::mojom::OptionalBool microphone = apps::mojom::OptionalBool::kUnknown;
apps::AppServiceProxyFactory::GetForProfile(profile())
->AppCapabilityAccessCache()
.ForOneApp(app_id, [&camera, &microphone](
const apps::CapabilityAccessUpdate& update) {
camera = update.Camera();
microphone = update.Microphone();
});
EXPECT_EQ(camera, accessing_camera);
EXPECT_EQ(microphone, accessing_microphone);
}

protected:
base::test::ScopedFeatureList scoped_feature_list_;

Expand Down Expand Up @@ -447,6 +477,108 @@ TEST_F(PublisherTest, ArcAppsOnApps) {
arc_apps->Shutdown();
}

TEST_F(PublisherTest, ArcApps_CapabilityAccess) {
ArcAppTest arc_test;
arc_test.SetUp(profile());
AppServiceProxyFactory::GetForProfile(profile())->FlushMojoCallsForTesting();
ArcApps* arc_apps = apps::ArcAppsFactory::GetForProfile(profile());
ASSERT_TRUE(arc_apps);

const auto& fake_apps = arc_test.fake_apps();
std::string package_name1 = fake_apps[0]->package_name;
std::string package_name2 = fake_apps[1]->package_name;

// Install fake apps.
arc_test.app_instance()->SendRefreshAppList(arc_test.fake_apps());

// Set accessing Camera for `package_name1`.
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
AppServiceProxyFactory::GetForProfile(profile())
->FlushMojoCallsForTesting();
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[0]),
/*accessing_camera=*/apps::mojom::OptionalBool::kTrue,
/*accessing_microphone=*/apps::mojom::OptionalBool::kUnknown);
}

// Cancel accessing Camera for `package_name1`.
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
AppServiceProxyFactory::GetForProfile(profile())
->FlushMojoCallsForTesting();
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[0]),
/*accessing_camera=*/apps::mojom::OptionalBool::kFalse,
/*accessing_microphone=*/apps::mojom::OptionalBool::kFalse);
}

// Set accessing Camera and Microphone for `package_name1`, and accessing
// Camera for `package_name2`.
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::MICROPHONE, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name2));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
AppServiceProxyFactory::GetForProfile(profile())
->FlushMojoCallsForTesting();
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[0]),
/*accessing_camera=*/apps::mojom::OptionalBool::kTrue,
/*accessing_microphone=*/apps::mojom::OptionalBool::kTrue);
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[1]),
/*accessing_camera=*/apps::mojom::OptionalBool::kTrue,
/*accessing_microphone=*/apps::mojom::OptionalBool::kUnknown);
}

// Cancel accessing Microphone for `package_name1`.
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name2));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
AppServiceProxyFactory::GetForProfile(profile())
->FlushMojoCallsForTesting();
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[0]),
/*accessing_camera=*/apps::mojom::OptionalBool::kTrue,
/*accessing_microphone=*/apps::mojom::OptionalBool::kFalse);
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[1]),
/*accessing_camera=*/apps::mojom::OptionalBool::kTrue,
/*accessing_microphone=*/apps::mojom::OptionalBool::kFalse);
}

// Cancel accessing CAMERA for `package_name1` and `package_name2`.
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
AppServiceProxyFactory::GetForProfile(profile())
->FlushMojoCallsForTesting();
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[0]),
/*accessing_camera=*/apps::mojom::OptionalBool::kFalse,
/*accessing_microphone=*/apps::mojom::OptionalBool::kFalse);
VerifyCapabilityAccess(
ArcAppTest::GetAppId(*fake_apps[1]),
/*accessing_camera=*/apps::mojom::OptionalBool::kFalse,
/*accessing_microphone=*/apps::mojom::OptionalBool::kFalse);
}

arc_apps->Shutdown();
}

TEST_F(PublisherTest, BuiltinAppsOnApps) {
// Verify Builtin apps are added to AppRegistryCache.
for (const auto& internal_app : app_list::GetInternalAppList(profile())) {
Expand Down

0 comments on commit e6b7511

Please sign in to comment.