chromium/chrome/browser/media/prefs/capture_device_ranking.h

// 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.

#ifndef CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_
#define CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_

#include "chrome/browser/media/prefs/pref_names.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "media/audio/audio_device_description.h"
#include "media/capture/video/video_capture_device_info.h"
#include "third_party/blink/public/common/mediastream/media_devices.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"

#include <string>

namespace media_prefs::internal {

// Returns a stable device id given `device_info`. This isn't guaranteed
// to be unique, but it is stable across reboots and device plug/unplug.
std::string DeviceInfoToStableId(
    const media::VideoCaptureDeviceInfo& device_info);
std::string DeviceInfoToStableId(
    const media::AudioDeviceDescription& device_info);
std::string DeviceInfoToStableId(const blink::WebMediaDeviceInfo& device_info);
std::string DeviceInfoToStableId(const blink::MediaStreamDevice& device_info);

// Returns a unique id given `device_info`. This is unique for the current
// session, but isn't guaranteed to be stable through reboots or device
// plug/unplug.
std::string DeviceInfoToUniqueId(
    const media::VideoCaptureDeviceInfo& device_info);
std::string DeviceInfoToUniqueId(
    const media::AudioDeviceDescription& device_info);
std::string DeviceInfoToUniqueId(const blink::WebMediaDeviceInfo& device_info);
std::string DeviceInfoToUniqueId(const blink::MediaStreamDevice& device_info);

}  // namespace media_prefs::internal

namespace {

DeviceInfoToStableId;
DeviceInfoToUniqueId;

// Returns the rank index for the passed `info`. If `info` doesn't have a
// rank then return size of `id_to_rank` because that will rank lower than
// any of the ranked devices.
template <typename T>
size_t GetRank(const T& info,
               const base::flat_map<std::string, size_t>& id_to_rank) {}

// Map device ids to a numeric ranking. Smaller values are more preferred.
template <typename Iterator>
base::flat_map<std::string, size_t> GetIdToRankMap(Iterator begin,
                                                   Iterator end) {}

template <typename T>
const base::Value::List& GetAndMaybeMigratePref(PrefService& prefs,
                                                const std::string& pref_name,
                                                std::vector<T>& device_infos);

// Reorders the passed `device_infos`, so that ranked devices are ordered at the
// beginning of the list. Devices don't have a ranking will retain their
// ordering, but will be shifted below ranked devices.
//
// This must be run on the UI thread as it reads from the PrefService.
template <typename T>
void PreferenceRankDeviceInfos(PrefService& prefs,
                               const std::string& pref_name,
                               std::vector<T>& device_infos) {}

// Updates device ranking given a preferred device and the list of competing
// devices.
//
// The algorithm is as follows:
// 1. Identify the best ranked device among the current devices
//   a. If a device isn't found in the ranking list, then it is appended
// 2. If the best ranked device is the preferred device, then exit
// 3. Else, move the preferred device to be just before the best ranked device
//
// The goal of the algorithm is to ensure that given the same set of devices we
// would get the same device ranking.
//
// Example case:
// Update 1 -
// The user selects the USB mic as preferred, and the other current
// devices are at the end of the list in the order enumerated by the OS.
// current ranking: []
// preferred_device_iter: 'USB Camera Mic',
// current_device_infos: 'bluetooth mic', 'builtin mic', 'USB Camera Mic'
//
// updated ranking: 'USB Camera Mic', 'bluetooth mic', 'builtin mic'
//
// Update 2 -
// The user disconnects the USB camera because they left their desk
// and they select the 'builtin mic' as preferred.
// current ranking: 'USB Camera Mic', 'builtin mic', 'bluetooth mic'
// preferred_device_iter: 'builtin mic'
// current_deivce_infos: 'bluetooth mic', 'builtin mic'
//
// updated ranking: 'USB Camera Mic', 'builtin mic', 'bluetooth mic'
//
// Note that the second update leaves the 'USB Camera Mic' as preferred over the
// other two mics because it wasn't present to be considered.
//
// This must be run on the UI thread as it read/writes to the PrefService.
template <typename T>
void UpdateDevicePreferenceRanking(
    PrefService& prefs,
    const std::string& pref_name,
    const typename std::vector<T>::const_iterator preferred_device_iter,
    const std::vector<T>& current_device_infos) {}

// Get the value of the ranking pref.
//
// If the ranking pref is unset, it will be initialized from the default device
// pref. If the default device pref is unset or the device isn't in
// `device_infos`, then the ranking pref will remain uninitialized.
// `pref_name` is required to be in {kAudioInputUserPreferenceRanking,
// kVideoInputUserPreferenceRanking}.
//
// Example scenarios:
// For all scenarios the user has set dev2 as the default and updated the
// browser.
//
// Scenario A:
//  1) User connects [dev1, dev2]
//  2) User visits a website that calls enumerateDevices or getUserMedia
//  3) Device ranking pref is updated to [dev2, dev1]
//  4) All subsequent usages refer to the device ranking pref and ignore legacy
//     default pref
//
// Scenario B:
//  1) User connects [dev1]
//  2) User visits a website that calls enumerateDevices or getUserMedia
//  3) Device ranking pref remains uninitialized because the default device
//     isn't present
//  4) User connects [dev1, dev2]
//  5) User visits a website that calls enumerateDevices or getUserMedia
//  6) Device ranking pref is updated to [dev2, dev1]
//  7) All subsequent usages refer to the device ranking pref and
//     ignore legacy default pref.
//
// Scenario C:
//  1) User connects [dev1]
//  2) User visits a website that calls enumerateDevices or getUserMedia
//  3) Device ranking pref remains uninitialized because the default device
//     isn't present
//  4) User expresses preference for dev1 in permission bubble or
//     Chrome settings
//  5) Device ranking pref is updated to [dev1]
//  6) All subsequent usages refer to the device ranking pref and ignore legacy
//     default pref.
//
// TODO(crbug.com/311205211): Remove this special initialization logic once the
// default device pref is removed.
template <typename T>
const base::Value::List& GetAndMaybeMigratePref(PrefService& prefs,
                                                const std::string& pref_name,
                                                std::vector<T>& device_infos) {}

}  // namespace

namespace media_prefs {

void RegisterUserPrefs(PrefRegistrySimple* registry);

template <typename T>
void PreferenceRankAudioDeviceInfos(PrefService& prefs,
                                    std::vector<T>& device_infos) {}

template <typename T>
void PreferenceRankVideoDeviceInfos(PrefService& prefs,
                                    std::vector<T>& device_infos) {}

template <typename T>
void UpdateAudioDevicePreferenceRanking(
    PrefService& prefs,
    const typename std::vector<T>::const_iterator preferred_device_iter,
    const std::vector<T>& current_device_infos) {}

template <typename T>
void UpdateVideoDevicePreferenceRanking(
    PrefService& prefs,
    const typename std::vector<T>::const_iterator preferred_device_iter,
    const std::vector<T>& current_device_infos) {}

}  // namespace media_prefs

#endif  // CHROME_BROWSER_MEDIA_PREFS_CAPTURE_DEVICE_RANKING_H_