chromium/chromeos/ash/services/device_sync/public/mojom/device_sync.mojom

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module ash.device_sync.mojom;

import "chromeos/ash/components/multidevice/mojom/multidevice_types.mojom";
import "mojo/public/mojom/base/time.mojom";

// Denotes the outcome of an HTTP request, where results like kBadRequest map
// onto HTTP error codes while others like kRequestServiceNotYetInitialized
// reflect issues adjacent to the actual HTTP request.
enum NetworkRequestResult {
  // Successful network request.
  kSuccess,

  // The network request itself was successful, but it did not produce the
  // expected result (e.g., called SetSoftwareFeatureState(), but the call did
  // not result in the feature state changing).
  kRequestSucceededButUnexpectedResult,

  // Service was not yet initialized; could not complete request.
  kServiceNotYetInitialized,

  // Request could not be completed because the device is offline or has issues
  // sending the HTTP request.
  kOffline,

  // Server endpoint could not be found.
  kEndpointNotFound,

  // Authentication error contacting back-end.
  kAuthenticationError,

  // Request was invalid.
  kBadRequest,

  // The server responded, but the response was not formatted correctly.
  kResponseMalformed,

  // Internal server error.
  kInternalServerError,

  // Unknown result.
  kUnknown
};

// Describes the desired state of a device's software feature.
enum FeatureStatusChange {
  // Enables a feature on a device and disables the feature on all other
  // devices associated with the account.
  kEnableExclusively,

  // Enables a feature on a device; other devices on the account are
  // unaffected.
  kEnableNonExclusively,

  // Disables a feature on a device; other devices on the account are
  // unaffected.
  kDisable
};

// Denotes the relevant CryptAuth service.
enum CryptAuthService {
  kEnrollment,
  kDeviceSync
};

// Describes a device's network reachability.
enum ConnectivityStatus {
  kOnline,
  kOffline,
  kUnknownConnectivity
};

// Describes the status of the encrypted group private key received in the
// SyncMetadataResponse.
enum GroupPrivateKeyStatus {
  // When Device Sync is not initialized, it cannot access the group private key
  // status and will return this value.
  kStatusUnavailableBecauseDeviceSyncIsNotInitialized,

  // When the CryptAuthV2 device manager hasn't initialized a device syncer, it
  // cannot access the group private key status and will return this value.
  kStatusUnavailableBecauseNoDeviceSyncerSet,

  // The CryptAuth SyncMetadata response that includes the encrypted group
  // private key hasn't been received yet.
  kWaitingForGroupPrivateKey,

  // The SyncMetadata response was been received, but doesn't include any
  // encrypted group private key. This is expected when no other user device
  // uploaded the key or if we already own the key.
  kNoEncryptedGroupPrivateKeyReceived,

  // The SyncMetadata response was received, but the included encrypted group
  // private key is empty.
  kEncryptedGroupPrivateKeyEmpty,

  // This device's CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether key is
  // missing, so the encrypted group private key cannot be decrypted.
  kLocalDeviceSyncBetterTogetherKeyMissing,

  // An error occurred when decrypting the group private key.
  kGroupPrivateKeyDecryptionFailed,

  // The group private key was successfully decrypted. This is the expected
  // final state of this flow.
  kGroupPrivateKeySuccessfullyDecrypted
};

// Describes the status of the better together remote device metadata.
enum BetterTogetherMetadataStatus {
  // When Device Sync is not initialized, it cannot access the better together
  // metadata status and will return this value.
  kStatusUnavailableBecauseDeviceSyncIsNotInitialized,

  // When the CryptAuthV2 device manager hasn't initialized a device syncer, it
  // cannot access the better together metadata status and will return this
  // value.
  kStatusUnavailableBecauseNoDeviceSyncerSet,

  // The attempt to process the encrypted device metadata hasn't started yet.
  // If the device sync attempt finishes and this is still the metadata
  // status, clients can inspect GroupPrivateKeyStatus to understand why.
  kWaitingToProcessDeviceMetadata,

  // The group private key required to decrypt the metadata is missing.
  // Clients can inspect GroupPrivateKeyStatus to understand why the group
  // private key is missing.
  kGroupPrivateKeyMissing,

  // CryptAuth didn't send any encrypted metadata.
  kEncryptedMetadataEmpty,

  // Device metadata was decrypted. This is the expected final state of this
  // flow.
  kMetadataDecrypted
};

struct FindEligibleDevicesResponse {
  array<ash.multidevice.mojom.RemoteDevice> eligible_devices;
  array<ash.multidevice.mojom.RemoteDevice> ineligible_devices;
};

struct DeviceActivityStatus {
  string device_id;
  mojo_base.mojom.Time last_activity_time;
  ConnectivityStatus connectivity_status;
  mojo_base.mojom.Time last_update_time;
};

struct DebugInfo {
  // Enrollment stats:
  mojo_base.mojom.Time last_enrollment_time;
  mojo_base.mojom.TimeDelta time_to_next_enrollment_attempt;
  bool is_recovering_from_enrollment_failure;
  bool is_enrollment_in_progress;

  // Sync stats:
  mojo_base.mojom.Time last_sync_time;
  mojo_base.mojom.TimeDelta time_to_next_sync_attempt;
  bool is_recovering_from_sync_failure;
  bool is_sync_in_progress;
};

interface DeviceSyncObserver {
  // Invoked when the current device has successfully completed enrollment. Note
  // that enrollment occurs once when the device first starts up, then the
  // device re-enrolls periodically (and on-command when ForceEnrollmentNow() is
  // called).
  OnEnrollmentFinished();

  // Invoked when new devices have been synced from the server. Note that this
  // function is not invoked if a device sync failed or if a device sync
  // succeeded but did not result in a change of devices.
  OnNewDevicesSynced();
};

// TODO(khorimoto): Flesh out API.
// Calls to this API run in the browser process.
interface DeviceSync {
  // Adds an Observer of this API.
  AddObserver(pending_remote<DeviceSyncObserver> observer) => ();

  // Triggers an enrollment; result is relayed via the OnEnrollmentFinished()
  // observer function. Returns whether the call could be completed
  // successfully. If the function returns false, this means that the device has
  // not yet completed registration with the back-end; clients should observe
  // this service and wait for an OnEnrollmentFinished() callback before
  // retrying.
  [Sync]
  ForceEnrollmentNow() => (bool success);

  // Triggers a device sync; result is relayed via the OnDevicesSynced()
  // observer function. Returns whether the call could be completed
  // successfully. If the function returns false, this means that the device has
  // not yet completed registration with the back-end; clients should observe
  // this service and wait for an OnEnrollmentFinished() callback before
  // retrying.
  [Sync]
  ForceSyncNow() => (bool success);

  // Returns the status of the encrypted group private key received in the
  // SyncMetadataResponse.
  GetGroupPrivateKeyStatus() => (GroupPrivateKeyStatus status);

  // Returns the status of the better together remote device metadata.
  GetBetterTogetherMetadataStatus() => (BetterTogetherMetadataStatus status);

  // Returns all synced devices associated with the primary account. If this
  // device has not yet registered with the back-end, no list is provided.
  GetSyncedDevices() =>
      (array<ash.multidevice.mojom.RemoteDevice>? devices);

  // Returns the RemoteDevice object associated with this device. If this device
  // has not yet registered with the back-end, no device is provided.
  GetLocalDeviceMetadata() =>
      (ash.multidevice.mojom.RemoteDevice? local_device);

  // Enables or disables the given software feature for the device with the
  // given public key. If |enabled| and |is_exclusive| are both true, this
  // function will enable the feature for the given device and disable the
  // feature for any other device which currently has that feature enabled.
  // |is_exclusive| is ignored if |enabled| is false.
  //
  // On success, this function returns a null error_code to the callback; on
  // error, it returns a valid error_code string indicating the reason for
  // failure.
  //
  // Note: In the special case of passing |software_feature| =
  // SoftwareFeature::EASY_UNLOCK_HOST and |enabled| = false, |public_key| is
  // ignored, because that combination of arguments disables EASY_UNLOCK_HOST on
  // all of the user's devices.
  //
  // TODO(crbug.com/40105247): Remove this function when v1 DeviceSync
  // is disabled.
  SetSoftwareFeatureState(
      string device_public_key,
      ash.multidevice.mojom.SoftwareFeature software_feature,
      bool enabled,
      bool is_exclusive) => (NetworkRequestResult result_code);

  // Enables or disables |feature| for the device with Instance ID
  // |device_instance_id|.
  //
  // This function can only affect devices using CryptAuth v2 DeviceSync since
  // it requires an Instance ID. See SetSoftwareFeatureState() for the v1
  // DeviceSync analog.
  SetFeatureStatus(
      string device_instance_id,
      ash.multidevice.mojom.SoftwareFeature feature,
      FeatureStatusChange status_change) => (NetworkRequestResult result_code);

  // Finds devices which are eligible for the given feature. When this function
  // is invoked, a network request will be sent to each eligible device which
  // instructs that device to enable BLE advertising; thus, this function can be
  // used to bootstrap connections to these devices.
  //
  // On success, this function returns a null error_code with a valid response
  // to the callback; on error, it returns a valid error_code string indicating
  // the reason for failure along with a null response.
  //
  // TODO(crbug.com/40105247): Remove this function when v1 DeviceSync
  // is disabled.
  FindEligibleDevices(
      ash.multidevice.mojom.SoftwareFeature software_feature) =>
          (NetworkRequestResult result_code,
           FindEligibleDevicesResponse? response);

  // Sends a GCM message--via CryptAuth--to devices with Instance IDs
  // |device_instance_ids|. The message includes the CryptAuth
  // service--Enrollment or DeviceSync--and the feature
  // type--kBetterTogetherHost, for example--indicating the reason for the
  // notification; these are specified by |cryptauth_service| and |feature|,
  // respectively.
  NotifyDevices(
      array<string> device_instance_ids,
      CryptAuthService cryptauth_service,
      ash.multidevice.mojom.SoftwareFeature feature) =>
    (NetworkRequestResult result_code);

  // Retrieves the activity status of the user's devices. When this function
  // is invoked, a network request will be sent to the CryptAuth server to
  // retrieve the last time a device was used.
  //
  // On success, this function returns a null error_code with the activity
  // statuses to the callback; on error, it returns a valid error_code string
  // indicating the reason for failure along with a null activity statuses.
  //
  // This method is expected to be used by multidevice_setup service running
  // in the browser process.
  GetDevicesActivityStatus() =>
          (NetworkRequestResult result_code,
           array<DeviceActivityStatus>? device_activity_statuses);

  // Functions below are implemented for chrome://proximity-auth page, which is
  // intended for debugging purposes only.
  // TODO(khorimoto): Determine whether a new, debug-only interface should be
  //                  refactored out of DeviceSync.
  GetDebugInfo() => (DebugInfo? debug_info);
};