chromium/chrome/browser/ash/app_list/arc/arc_usb_host_permission_manager.h

// Copyright 2018 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_ASH_APP_LIST_ARC_ARC_USB_HOST_PERMISSION_MANAGER_H_
#define CHROME_BROWSER_ASH_APP_LIST_ARC_ARC_USB_HOST_PERMISSION_MANAGER_H_

#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "ash/components/arc/usb/usb_host_ui_delegate.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "components/keyed_service/core/keyed_service.h"

class Profile;

namespace arc {

class ArcUsbHostBridge;
class ArcUsbHostPermissionTest;

class ArcUsbHostPermissionManager : public ArcAppListPrefs::Observer,
                                    public ArcUsbHostUiDelegate,
                                    public KeyedService {
 public:
  struct UsbDeviceEntry {
    UsbDeviceEntry(const std::string& guid,
                   const std::u16string& device_name,
                   const std::u16string& serial_number,
                   uint16_t vendor_id,
                   uint16_t product_id);
    UsbDeviceEntry(const UsbDeviceEntry& other);
    UsbDeviceEntry& operator=(const UsbDeviceEntry& other);
    // Returns if the device entry is considered as persistent. Granted
    // permission for persistent device will persist when device is
    // removed.
    bool IsPersistent() const { return !serial_number.empty(); }
    // Checks if two device entries matches. If both devices are persistent,
    // check if their serial_number, vendor_id and product_id matches. Otherwise
    // check if therr guid matches.
    bool Matches(const UsbDeviceEntry& other) const;

    // This field can be null if device is persistent and the entry is restored
    // from Chrome prefs. But it can not be null if the entry is not persistent
    // or it is owned by UsbPermissionRequest.
    std::string guid;
    // Device name which is shown in the permission dialog.
    std::u16string device_name;
    // Serial_number of the device. If this field is null if device is
    // considered as non-persistent.
    std::u16string serial_number;
    // Vendor_id of the device.
    uint16_t vendor_id;
    // Product id of the device.
    uint16_t product_id;
  };

  class UsbPermissionRequest {
   public:
    UsbPermissionRequest(
        const std::string& package_name,
        bool is_scan_request,
        std::optional<UsbDeviceEntry> usb_device_entry,
        std::optional<ArcUsbHostUiDelegate::RequestPermissionCallback>
            callback);
    UsbPermissionRequest(UsbPermissionRequest&& other);
    UsbPermissionRequest& operator=(UsbPermissionRequest&& other);

    UsbPermissionRequest(const UsbPermissionRequest&) = delete;
    UsbPermissionRequest& operator=(const UsbPermissionRequest&) = delete;

    ~UsbPermissionRequest();

    const std::string& package_name() const { return package_name_; }
    bool is_scan_request() const { return is_scan_request_; }
    const std::optional<UsbDeviceEntry>& usb_device_entry() const {
      return usb_device_entry_;
    }

    // Runs |callback_| with |allowed|.
    void Resolve(bool allowed);

   private:
    // Name of the package that is currently requesting permission.
    std::string package_name_;
    // True if the this is a scan device list request. Otherwise it's a device
    // access request. Open to make it a enum if we have more types.
    bool is_scan_request_;
    // Device entry of targeting device access request. nullopt if this is a
    // scan device list request.
    std::optional<UsbDeviceEntry> usb_device_entry_;
    // Callback of the device access reqeust. nullopt if this is a scan device
    // list request.
    std::optional<RequestPermissionCallback> callback_;
  };

  ArcUsbHostPermissionManager(Profile* profile,
                              ArcAppListPrefs* arc_app_list_prefs,
                              ArcUsbHostBridge* arc_usb_host_bridge);
  ArcUsbHostPermissionManager(const ArcUsbHostPermissionManager&) = delete;
  ArcUsbHostPermissionManager& operator=(const ArcUsbHostPermissionManager&) =
      delete;

  ~ArcUsbHostPermissionManager() override;

  static ArcUsbHostPermissionManager* GetForBrowserContext(
      content::BrowserContext* context);

  // ArcUsbHostUiDelegate:
  void RequestUsbScanDeviceListPermission(
      const std::string& package_name,
      ArcUsbHostUiDelegate::RequestPermissionCallback callback) override;
  void RequestUsbAccessPermission(
      const std::string& package_name,
      const std::string& guid,
      const std::u16string& serial_number,
      const std::u16string& manufacturer_string,
      const std::u16string& product_string,
      uint16_t vendor_id,
      uint16_t product_id,
      ArcUsbHostUiDelegate::RequestPermissionCallback callback) override;
  bool HasUsbAccessPermission(const std::string& package_name,
                              const std::string& guid,
                              const std::u16string& serial_number,
                              uint16_t vendor_id,
                              uint16_t product_id) const override;
  void GrantUsbAccessPermission(const std::string& package_name,
                                const std::string& guid,
                                uint16_t vendor_id,
                                uint16_t product_id) override;
  std::unordered_set<std::string> GetEventPackageList(
      const std::string& guid,
      const std::u16string& serial_number,
      uint16_t vendor_id,
      uint16_t product_id) const override;
  void DeviceRemoved(const std::string& guid) override;
  void ClearPermissionRequests() override;

  // ArcAppListPrefs::Observer:
  void OnPackageRemoved(const std::string& package_name,
                        bool uninstalled) override;

  const std::vector<UsbPermissionRequest>& GetPendingRequestsForTesting() {
    return pending_requests_;
  }

  // Clear |usb_scan_devicelist_permission_packages_| and
  // |usb_access_permission_dict_| for testing. Will not affect Chrome prefs.
  void ClearPermissionForTesting();

 private:
  friend class ArcUsbHostPermissionManagerFactory;
  friend class ArcUsbHostPermissionTest;

  static std::unique_ptr<ArcUsbHostPermissionManager> Create(
      content::BrowserContext* context);

  // Restores granted permissions. Called in constructor. Device list scan
  // permission and device access permission for persistent devices will be
  // restored.
  void RestorePermissionFromChromePrefs();

  // Tries to process next permission request as when permission dialog is
  // available.
  void MaybeProcessNextPermissionRequest();

  bool HasUsbScanDeviceListPermission(const std::string& package_name) const;

  bool HasUsbAccessPermission(const std::string& package_name,
                              const UsbDeviceEntry& usb_device_entry) const;

  // Callback for UI permission dialog.
  void OnUsbPermissionReceived(UsbPermissionRequest request, bool allowed);

  void UpdateArcUsbScanDeviceListPermission(const std::string& package_name,
                                            bool allowed);

  void UpdateArcUsbAccessPermission(const std::string& package_name,
                                    const UsbDeviceEntry& usb_device_entry,
                                    bool allowed);

  std::vector<UsbPermissionRequest> pending_requests_;

  // Package permissions will be removed when package is uninstalled.
  // Packages that have been granted permission to scan device list. It will
  // be also stored in Chrome prefs. We may need create UI to revoke this
  // permission.
  std::unordered_set<std::string> usb_scan_devicelist_permission_packages_;

  // Package permissions will be removed when package is uninstalled.
  // Dictory of granted package to devices access permission map.
  // Permissions granted to persistent devices persist when device is removed
  // while permissions granted to ephemeral devices will be removed in such
  // situation.
  std::unordered_multimap<std::string, UsbDeviceEntry>
      usb_access_permission_dict_;

  // Package which made the current USB reuqest.
  std::string current_requesting_package_;

  // Device GUID of targeting device of current USB request. Empty if it's a
  // scan request.
  std::string current_requesting_guid_;

  // True if the permission dialog is currently being shown. Any permission
  // request that occurs while this is true will be queued until after the user
  // has resolved the current request.
  bool is_permission_dialog_visible_ = false;

  const raw_ptr<Profile> profile_;

  const raw_ptr<ArcAppListPrefs> arc_app_list_prefs_;

  base::WeakPtrFactory<ArcUsbHostPermissionManager> weak_ptr_factory_{this};
};

}  // namespace arc

#endif  // CHROME_BROWSER_ASH_APP_LIST_ARC_ARC_USB_HOST_PERMISSION_MANAGER_H_