1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
ash / system / video_conference / video_conference_tray_controller.h [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_H_
#define ASH_SYSTEM_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_H_
#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/shelf/shelf.h"
#include "ash/shell_observer.h"
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h"
#include "ash/system/video_conference/video_conference_common.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "chromeos/crosapi/mojom/video_conference.mojom-forward.h"
#include "components/prefs/pref_registry_simple.h"
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
namespace base {
class UnguessableToken;
} // namespace base
namespace ash {
struct AnchoredNudgeData;
class VideoConferenceTray;
using MediaApps = std::vector<crosapi::mojom::VideoConferenceMediaAppInfoPtr>;
// Controller that will act as a "bridge" between VC apps management and the VC
// UI layers. The singleton instance is constructed immediately before and
// destructed immediately after the UI, so any code that keeps a reference to
// it must be prepared to accommodate this specific lifetime in order to prevent
// any use-after-free bugs.
class ASH_EXPORT VideoConferenceTrayController
: public media::CameraPrivacySwitchObserver,
public CrasAudioHandler::AudioObserver,
public SessionObserver,
public ShellObserver {
public:
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
// Called when the state of `has_media_app` within
// `VideoConferenceMediaState` is changed.
virtual void OnHasMediaAppStateChange() = 0;
// Called when the state of camera permission is changed.
virtual void OnCameraPermissionStateChange() = 0;
// Called when the state of microphone permission is changed.
virtual void OnMicrophonePermissionStateChange() = 0;
// Called when the state of camera capturing is changed.
virtual void OnCameraCapturingStateChange(bool is_capturing) = 0;
// Called when the state of microphone capturing is changed.
virtual void OnMicrophoneCapturingStateChange(bool is_capturing) = 0;
// Called when the state of screen sharing is changed.
virtual void OnScreenSharingStateChange(bool is_capturing_screen) = 0;
// Called when the Dlc download state is changed for `feature_tile_title` if
// any DLC was registered for that effect.
virtual void OnDlcDownloadStateChanged(
bool error,
const std::u16string& feature_tile_title) = 0;
};
VideoConferenceTrayController();
VideoConferenceTrayController(const VideoConferenceTrayController&) = delete;
VideoConferenceTrayController& operator=(
const VideoConferenceTrayController&) = delete;
~VideoConferenceTrayController() override;
// Called inside ash/ash_prefs.cc to register related prefs.
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Returns the singleton instance.
static VideoConferenceTrayController* Get();
// Adds this class as an observer for CrasAudioHandler and
// CameraHalDispatcherImpl.
// (1) We should not call this in /ash/system/* tests, because we are not
// using FakeCrasAudioClient or MockCameraHalServer. Currently, we directly
// mock the VideoConferenceTrayButtons inside
// FakeVideoConferenceTrayController; which is a simpler approach.
// (2) We need this initialization in
// ChromeBrowserMainExtraPartsAsh::PreProfileInit for production code.
void Initialize(VideoConferenceManagerBase* video_conference_manager);
// Observer functions.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Whether the tray should be shown.
bool ShouldShowTray() const;
// Caches a nudge data object for nudges that attempt to show while the tray
// is animating in, so they only show once the tray animation has ended. The
// request will be run immediately if the tray is not animating.
void CreateNudgeRequest(std::unique_ptr<AnchoredNudgeData> nudge_data);
// Shows the cached `requested_nudge_data_` object, if one exists.
void MaybeRunNudgeRequest();
// Attempts showing the speak-on-mute opt-in nudge.
void MaybeShowSpeakOnMuteOptInNudge();
// Returns true if we can show the animation to help users to discover the new
// feature.
bool ShouldShowImageButtonAnimation() const;
bool ShouldShowCreateWithAiButtonAnimation() const;
// Disables showing the animation for the button from now on. Calling the
// above ShouldShow...() will return false for the current active user going
// forward.
void DismissImageButtonAnimationForever();
void DismissCreateWithAiButtonAnimationForever();
// Callback used to update prefs whenever a user opts in or out of the
// speak-on-mute feature. An `opt_in` value of false means the user opted out.
void OnSpeakOnMuteNudgeOptInAction(bool opt_in);
void OnDlcDownloadStateFetched(bool add_warning,
const std::u16string& feature_tile_title);
// Closes all nudges that are shown anchored to the VC tray, if any.
void CloseAllVcNudges();
// Returns whether `state_` indicates permissions are granted for different
// mediums.
bool GetHasCameraPermissions() const;
bool GetHasMicrophonePermissions() const;
// Returns whether `state_` indicates a capture session is in progress for
// different mediums.
bool IsCapturingScreen() const;
bool IsCapturingCamera() const;
bool IsCapturingMicrophone() const;
// Sets the state for camera mute. Virtual for testing/mocking.
virtual void SetCameraMuted(bool muted);
// Gets the state for camera mute. Virtual for testing/mocking.
virtual bool GetCameraMuted();
// Sets the state for microphone mute. Virtual for testing/mocking.
virtual void SetMicrophoneMuted(bool muted);
// Gets the state for microphone mute. Virtual for testing/mocking.
virtual bool GetMicrophoneMuted();
// Stops all screen sharing. Virtual for testing/mocking.
virtual void StopAllScreenShare();
// Returns asynchronously a vector of media apps that will be displayed in the
// "Return to app" panel of the bubble. Virtual for testing/mocking.
virtual void GetMediaApps(base::OnceCallback<void(MediaApps)> ui_callback);
// Brings the app with the given `id` to the foreground.
virtual void ReturnToApp(const base::UnguessableToken& id);
// Updates the tray UI with the given `VideoConferenceMediaState`.
void UpdateWithMediaState(VideoConferenceMediaState state);
// Returns true if any running media apps have been granted permission for
// camera/microphone.
bool HasCameraPermission() const;
bool HasMicrophonePermission() const;
// Enable or disable input stream ewma power report.
void SetEwmaPowerReportEnabled(bool enabled);
// Return the last reported ewma power.
double GetEwmaPower();
// Enable or disable sidetone.
void SetSidetoneEnabled(bool enabled);
// Gets the state for sidetone.
bool GetSidetoneEnabled() const;
// Gets whether sidetone is supported.
bool IsSidetoneSupported() const;
// Update the sidetone supported value.
// Should be called before calling IsSidetoneSupported.
void UpdateSidetoneSupportedState();
// Handles device usage from a VC app while the device is system disabled.
virtual void HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice device,
const std::u16string& app_name);
// media::CameraPrivacySwitchObserver:
void OnCameraHWPrivacySwitchStateChanged(
const std::string& device_id,
cros::mojom::CameraPrivacySwitchState state) override;
void OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState state) override;
// CrasAudioHandler::AudioObserver:
void OnInputMuteChanged(
bool mute_on,
CrasAudioHandler::InputMuteChangeMethod method) override;
// CrasAudioHandler::AudioObserver:
// Pop up a toast when speaking on mute is detected.
void OnSpeakOnMuteDetected() override;
// SessionObserver:
void OnUserSessionAdded(const AccountId& account_id) override;
// ShellObserver:
void OnShellDestroying() override;
// Handles client updates such as a change of title or addition / removal of a
// VC app. Virtual to allow mock classes to override for testing.
virtual void HandleClientUpdate(
crosapi::mojom::VideoConferenceClientUpdatePtr update);
// Handles showing the shelf when a new app is added.
void OnAppAdded();
// Gets `disable_shelf_autohide_timer_`, used for testing.
base::OneShotTimer& GetShelfAutoHideTimerForTest();
virtual VideoConferenceTrayEffectsManager& GetEffectsManager();
// Passes create background image action to `video_conference_manager_`.
void CreateBackgroundImage();
bool camera_muted_by_hardware_switch() const {
return camera_muted_by_hardware_switch_;
}
bool camera_muted_by_software_switch() const {
return camera_muted_by_software_switch_;
}
bool initialized() const { return initialized_; }
private:
// All the types of the use while disabled nudge.
enum class UsedWhileDisabledNudgeType {
kCamera = 0,
kMicrophone = 1,
kBoth = 2,
kMaxValue = kBoth
};
// Updates the state of the camera icons across all `VideoConferenceTray`.
void UpdateCameraIcons();
// Records repeated shows metric when the timer is stop.
void RecordRepeatedShows();
// Returns true if any of the VC nudges are visible on screen.
bool IsAnyVcNudgeShown();
// Displays the use while disabled nudge according to the given `type`.
void DisplayUsedWhileDisabledNudge(UsedWhileDisabledNudgeType type,
const std::u16string& app_name);
UsedWhileDisabledNudgeType GetUsedWhileDisabledNudgeType(
crosapi::mojom::VideoConferenceMediaDevice device);
// This keeps track the current VC media state. The state is being updated by
// `UpdateWithMediaState()`, calling from `VideoConferenceManagerAsh`.
VideoConferenceMediaState state_;
// This keeps track of the current Camera Privacy Switch state.
// Updated via `OnCameraHWPrivacySwitchStateChanged()` and
// `OnCameraSWPrivacySwitchStateChanged()` Fetching this would otherwise take
// an asynchronous call to `media::CameraHalDispatcherImpl`.
bool camera_muted_by_hardware_switch_ = false;
bool camera_muted_by_software_switch_ = false;
// True if microphone is muted by the hardware switch, false if the microphone
// is muted through software. If the microphone is not muted, disregards this
// value.
bool microphone_muted_by_hardware_switch_ = false;
// Timer responsible for hiding the shelf after it has been shown to alert the
// user of a new app accessing the sensors.
base::OneShotTimer disable_shelf_autohide_timer_;
// List of locks which force the shelf to show, if the shelf is autohidden.
std::list<Shelf::ScopedDisableAutoHide> disable_shelf_autohide_locks_;
// Used by the views to construct and lay out effects in the bubble.
VideoConferenceTrayEffectsManager effects_manager_;
// Registered observers.
base::ObserverList<Observer> observer_list_;
// The last time speak-on-mute nudge shown.
// The cool down periods for nudges:
// 1. No cool down for the first nudge,
// 2. 2 mins for the second nudge,
// 3. 4 mins for the third nudge,
// 4. 8 mins for the forth nudge.
base::TimeTicks last_speak_on_mute_nudge_shown_time_;
// The counter of how many time the speak-on-mute nudge has shown in the
// current session.
int speak_on_mute_nudge_shown_count_ = 0;
// `video_conference_manager_` should be valid after `initialized_`.
// Currently, `VideoConferenceTrayController` is destroyed inside
// `ChromeBrowserMainParts::PostMainMessageLoopRun()` as a chrome_extra_part;
// `VideoConferenceManagerAsh` is destroyed inside crosapi_manager_.reset()
// which is after `VideoConferenceTrayController`.
raw_ptr<VideoConferenceManagerBase> video_conference_manager_ = nullptr;
bool initialized_ = false;
// Used to record metrics of repeated shows per 100 ms.
int count_repeated_shows_ = 0;
base::DelayTimer repeated_shows_timer_;
// Due to some constraint in `VideoConferenceManagerAsh`, when both microphone
// and camera is being accessed when disabled,`HandleDeviceUsedWhileDisabled`
// will be called twice for each device. Thus, we need to wait for both 2
// calls and display one nudge for both. These are the timer and the cache
// type to make that happen.
base::OneShotTimer use_while_disabled_signal_waiter_;
UsedWhileDisabledNudgeType use_while_disabled_nudge_on_wait_;
// The contents of a nudge data object that is cached so it can be shown once
// the tray has fully animated in.
std::unique_ptr<AnchoredNudgeData> requested_nudge_data_;
base::WeakPtrFactory<VideoConferenceTrayController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_H_