Skip to content

Commit

Permalink
privacy-hub: Setup browser and frontend communication
Browse files Browse the repository at this point in the history
- Setup communication between the browser process and front end to
  display the list of applications in the Privacy controls sensor
  subpages
- Display the list of applications in the microphone subpage with a
  dummy UI

Bug: b:305006304
Change-Id: I28d63429364e6dde9e299224f14e4b758093d4bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4935211
Reviewed-by: Wes Okuhara <wesokuhara@google.com>
Reviewed-by: Tim Sergeant <tsergeant@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Md Shahadat Hossain Shahin <shahinmd@google.com>
Cr-Commit-Position: refs/heads/main@{#1212177}
  • Loading branch information
Md Shahadat Hossain Shahin authored and Chromium LUCI CQ committed Oct 19, 2023
1 parent e699805 commit 5db996b
Show file tree
Hide file tree
Showing 19 changed files with 636 additions and 1 deletion.
3 changes: 3 additions & 0 deletions chrome/app/os_settings_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -6180,6 +6180,9 @@ Press &amp; hold keyboard keys to see accent marks and special characters. This
<message name="IDS_OS_SETTINGS_PRIVACY_HUB_NO_WEBSITE_CAN_USE_MIC_TEXT" translateable="false" desc="The text displayed in the Websites section of the microphone subpage when microphone is not allowed.">
No website is allowed to use your microphone
</message>
<message name="IDS_OS_SETTINGS_PRIVACY_HUB_APPS_SECTION_TITLE" translateable="false" desc="The title of the Apps section of the privacy hub sensor subpages.">
Apps
</message>


<!-- On Startup (OS settings) -->
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -5304,6 +5304,7 @@ static_library("browser") {
"//chrome/browser/ui/webui/ash/settings/pages/apps/mojom",
"//chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings:mojom",
"//chrome/browser/ui/webui/ash/settings/pages/files/mojom",
"//chrome/browser/ui/webui/ash/settings/pages/privacy/mojom",
"//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/ui/webui/ash/vm:mojo_bindings",
"//chrome/browser/ui/webui/ash/web_app_install:mojo_bindings",
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -4942,6 +4942,7 @@ source_set("unit_tests") {
"../ui/webui/ash/settings/pages/multidevice/multidevice_handler_unittest.cc",
"../ui/webui/ash/settings/pages/multidevice/multidevice_section_unittest.cc",
"../ui/webui/ash/settings/pages/os_settings_section_unittest.cc",
"../ui/webui/ash/settings/pages/privacy/app_permission_handler_unittest.cc",
"../ui/webui/ash/settings/pages/privacy/metrics_consent_handler_unittest.cc",
"../ui/webui/ash/settings/pages/privacy/privacy_hub_handler_unittest.cc",
"../ui/webui/ash/settings/pages/search/search_engines_handler_unittest.cc",
Expand Down Expand Up @@ -6049,6 +6050,7 @@ source_set("unit_tests") {
"//chrome/browser/ui/ash/holding_space:test_support",
"//chrome/browser/ui/webui/ash/settings/pages/apps/mojom",
"//chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings:mojom",
"//chrome/browser/ui/webui/ash/settings/pages/privacy/mojom",
"//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/chrome_browser_interface_binders.cc
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
#include "chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings/input_device_settings_provider.mojom.h"
#include "chrome/browser/ui/webui/ash/settings/pages/files/mojom/google_drive_handler.mojom.h"
#include "chrome/browser/ui/webui/ash/settings/pages/files/mojom/one_drive_handler.mojom.h"
#include "chrome/browser/ui/webui/ash/settings/pages/privacy/mojom/app_permission_handler.mojom.h"
#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
#include "chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom.h"
#include "chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.h"
Expand Down Expand Up @@ -1354,6 +1355,10 @@ void PopulateChromeWebUIFrameBinders(
ash::settings::app_notification::mojom::AppNotificationsHandler,
ash::settings::OSSettingsUI>(map);

RegisterWebUIControllerInterfaceBinder<
ash::settings::app_permission::mojom::AppPermissionsHandler,
ash::settings::OSSettingsUI>(map);

RegisterWebUIControllerInterfaceBinder<
ash::settings::mojom::InputDeviceSettingsProvider,
ash::settings::OSSettingsUI>(map);
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/resources/ash/settings/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ build_webui("build") {
"//chrome/browser/ui/webui/ash/settings/pages/apps/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings:mojom_ts__generator",
"//chrome/browser/ui/webui/ash/settings/pages/files/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/ash/settings/pages/privacy/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/ash/settings/search/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/settings/ash/display_settings:mojom_ts__generator",
"//chromeos/ash/components/audio/public/mojom:mojom_ts__generator",
Expand All @@ -482,6 +483,7 @@ build_webui("build") {
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings/input_device_settings_provider.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/pages/files/mojom/google_drive_handler.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/pages/files/mojom/one_drive_handler.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/pages/privacy/mojom/app_permission_handler.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom-webui.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@
</div>
</template>
<div class="hr"></div>
<div id="appsSection">
<h2 id="appsSectionTitle">
$i18n{privacyHubAppsSectionTitle}
</h2>
<div id="appList" class="list-frame">
<template is="dom-repeat" items="[[appList_]]" as="app">
<div class="settings-box">
<div class="list-item start settings-box-text">
[[app.name]]
</div>
<cr-toggle
checked="[[isMicrophonePermissionEnabled_(app)]]">
</cr-toggle>
</div>
</template>
</div>
</div>
<div class="hr"></div>
<div id="websitesSection">
<h2 id="websitesSectionTitle">$i18n{websitesSectionTitle}</h2>
<cr-link-row
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,27 @@
* the state of the system microphone access.
*/

import {PermissionType} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
import {isPermissionEnabled} from 'chrome://resources/cr_components/app_management/permission_util.js';
import {PrefsMixin} from 'chrome://resources/cr_components/settings_prefs/prefs_mixin.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {App, AppPermissionsHandler, AppPermissionsHandlerInterface, AppPermissionsObserverReceiver} from '../mojom-webui/app_permission_handler.mojom-webui.js';

import {MediaDevicesProxy} from './media_devices_proxy.js';
import {PrivacyHubBrowserProxy, PrivacyHubBrowserProxyImpl} from './privacy_hub_browser_proxy.js';
import {getTemplate} from './privacy_hub_microphone_subpage.html.js';

/**
* Whether the app has microphone permission defined.
* */
function hasMicrophonePermission(app: App): boolean {
return app.permissions[PermissionType.kMicrophone] !== undefined;
}

const SettingsPrivacyHubMicrophoneSubpageBase =
WebUiListenerMixin(I18nMixin(PrefsMixin(PolymerElement)));

Expand All @@ -32,6 +44,14 @@ export class SettingsPrivacyHubMicrophoneSubpage extends

static get properties() {
return {
/**
* Apps with microphone permission.
*/
appList_: {
type: Array,
value: [],
},

/**
* The list of microphones connected to the device.
*/
Expand Down Expand Up @@ -67,16 +87,23 @@ export class SettingsPrivacyHubMicrophoneSubpage extends
};
}

private appList_: App[];
private appPermissionsObserverReceiver_: AppPermissionsObserverReceiver|null;
private browserProxy_: PrivacyHubBrowserProxy;
private connectedMicrophones_: string[];
private isMicListEmpty_: boolean;
private microphoneHardwareToggleActive_: boolean;
private connectedMicrophones_: string[];
private mojoInterfaceProvider_: AppPermissionsHandlerInterface;
private shouldDisableMicrophoneToggle_: boolean;

constructor() {
super();

this.browserProxy_ = PrivacyHubBrowserProxyImpl.getInstance();

this.mojoInterfaceProvider_ = AppPermissionsHandler.getRemote();

this.appPermissionsObserverReceiver_ = null;
}

override ready(): void {
Expand All @@ -96,10 +123,31 @@ export class SettingsPrivacyHubMicrophoneSubpage extends
'devicechange', () => this.updateMicrophoneList_());
}

override async connectedCallback(): Promise<void> {
super.connectedCallback();

this.appPermissionsObserverReceiver_ =
new AppPermissionsObserverReceiver(this);
this.mojoInterfaceProvider_.addObserver(
this.appPermissionsObserverReceiver_.$.bindNewPipeAndPassRemote());

await this.updateAppList_();
}

override disconnectedCallback(): void {
super.disconnectedCallback();
this.appPermissionsObserverReceiver_!.$.close();
}

private setMicrophoneHardwareToggleState_(enabled: boolean): void {
this.microphoneHardwareToggleActive_ = enabled;
}

private async updateAppList_(): Promise<void> {
const apps = (await this.mojoInterfaceProvider_.getApps()).apps;
this.appList_ = apps.filter(app => hasMicrophonePermission(app));
}

private async updateMicrophoneList_(): Promise<void> {
const connectedMicrophones: string[] = [];
const devices: MediaDeviceInfo[] =
Expand Down Expand Up @@ -138,6 +186,39 @@ export class SettingsPrivacyHubMicrophoneSubpage extends
private onManagePermissionsInChromeRowClick_(): void {
window.open('chrome://settings/content/microphone');
}

/**
* Returns true if the microphone permission of the app is in Allowed or
* equivalent state.
*/
private isMicrophonePermissionEnabled_(app: App): boolean {
assert(hasMicrophonePermission(app));
return isPermissionEnabled(
app.permissions[PermissionType.kMicrophone]!.value);
}

/** Implements AppPermissionsObserver.OnAppUpdated */
onAppUpdated(updatedApp: App): void {
if (!hasMicrophonePermission(updatedApp)) {
return;
}
const idx = this.appList_.findIndex(app => app.id === updatedApp.id);
if (idx === -1) {
// New app installed.
this.push('appList_', updatedApp);
} else {
// An already installed app is updated.
this.splice('appList_', idx, 1, updatedApp);
}
}

/** Implements AppPermissionsObserver.OnAppRemoved */
onAppRemoved(appId: string): void {
const idx = this.appList_.findIndex(app => app.id === appId);
if (idx !== -1) {
this.splice('appList_', idx, 1);
}
}
}

declare global {
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3324,6 +3324,8 @@ static_library("ui") {
"webui/ash/settings/pages/power/device_power_handler.h",
"webui/ash/settings/pages/printing/cups_printers_handler.cc",
"webui/ash/settings/pages/printing/cups_printers_handler.h",
"webui/ash/settings/pages/privacy/app_permission_handler.cc",
"webui/ash/settings/pages/privacy/app_permission_handler.h",
"webui/ash/settings/pages/privacy/metrics_consent_handler.cc",
"webui/ash/settings/pages/privacy/metrics_consent_handler.h",
"webui/ash/settings/pages/search/google_assistant_handler.cc",
Expand Down Expand Up @@ -3597,6 +3599,7 @@ static_library("ui") {
"//chrome/browser/ui/webui/ash/settings/pages/apps/mojom",
"//chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings:mojom",
"//chrome/browser/ui/webui/ash/settings/pages/files/mojom",
"//chrome/browser/ui/webui/ash/settings/pages/privacy/mojom",
"//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/ui/webui/ash/vm:mojo_bindings",
"//chrome/browser/ui/webui/ash/web_app_install:mojo_bindings",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/settings/pages/privacy/app_permission_handler.h"

#include "base/ranges/algorithm.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "components/services/app_service/public/cpp/permission.h"
#include "components/services/app_service/public/cpp/types_util.h"

namespace ash::settings {

namespace {

// Returns true if `permission` is one of {apps::PermissionType::kCamera,
// apps::PermissionType::kLocation, apps::PermissionType::kMicrophone}.
bool IsPermissionTypeRelevant(const apps::PermissionPtr& permission) {
return (permission->permission_type == apps::PermissionType::kCamera ||
permission->permission_type == apps::PermissionType::kLocation ||
permission->permission_type == apps::PermissionType::kMicrophone);
}

bool HasRelevantPermission(const apps::AppUpdate& update) {
return base::ranges::any_of(update.Permissions(), &IsPermissionTypeRelevant);
}

app_permission::mojom::AppPtr CreateAppPtr(const apps::AppUpdate& update) {
auto app = app_permission::mojom::App::New();
app->id = update.AppId();
app->name = update.Name();

for (const auto& permission : update.Permissions()) {
if (IsPermissionTypeRelevant(permission)) {
app->permissions[permission->permission_type] = permission->Clone();
}
}
return app;
}

} // namespace

using app_permission::mojom::AppPermissionsHandler;
using app_permission::mojom::AppPermissionsObserver;

AppPermissionHandler::AppPermissionHandler(
apps::AppServiceProxy* app_service_proxy)
: app_service_proxy_(app_service_proxy) {
app_registry_cache_observer_.Observe(&app_service_proxy_->AppRegistryCache());
}

AppPermissionHandler::~AppPermissionHandler() {}

void AppPermissionHandler::AddObserver(
mojo::PendingRemote<app_permission::mojom::AppPermissionsObserver>
observer) {
observer_list_.Add(std::move(observer));
}

void AppPermissionHandler::BindInterface(
mojo::PendingReceiver<app_permission::mojom::AppPermissionsHandler>
receiver) {
if (receiver_.is_bound()) {
receiver_.reset();
}
receiver_.Bind(std::move(receiver));
}

void AppPermissionHandler::GetApps(
base::OnceCallback<void(std::vector<app_permission::mojom::AppPtr>)>
callback) {
std::move(callback).Run(GetAppList());
}

void AppPermissionHandler::OnAppUpdate(const apps::AppUpdate& update) {
if (!HasRelevantPermission(update)) {
// This app is irrelevant for the Privacy controls sensor subpages.
return;
}

if (!apps_util::IsInstalled(update.Readiness()) ||
!update.ShowInManagement().value_or(true)) {
for (const auto& observer : observer_list_) {
observer->OnAppRemoved(update.AppId());
}
} else {
for (const auto& observer : observer_list_) {
observer->OnAppUpdated(CreateAppPtr(update));
}
}
}

std::vector<app_permission::mojom::AppPtr> AppPermissionHandler::GetAppList() {
std::vector<app_permission::mojom::AppPtr> apps;
app_service_proxy_->AppRegistryCache().ForEachApp(
[&apps](const apps::AppUpdate& update) {
if (update.ShowInManagement().value_or(false) &&
apps_util::IsInstalled(update.Readiness()) &&
HasRelevantPermission(update)) {
apps.push_back(CreateAppPtr(update));
}
});
return apps;
}

void AppPermissionHandler::OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) {
app_registry_cache_observer_.Reset();
}

} // namespace ash::settings

0 comments on commit 5db996b

Please sign in to comment.