chromium/chrome/browser/trusted_vault/trusted_vault_client_android.cc

// Copyright 2019 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/trusted_vault/trusted_vault_client_android.h"

#include <utility>

#include "base/android/jni_android.h"
#include "base/check_op.h"
#include "base/metrics/histogram_functions.h"
#include "base/not_fatal_until.h"
#include "base/notreached.h"
#include "components/sync/service/sync_service_utils.h"
#include "content/public/browser/browser_thread.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/TrustedVaultClient_jni.h"

TrustedVaultClientAndroid::OngoingFetchKeys::OngoingFetchKeys(
    const CoreAccountInfo& account_info,
    base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> callback)
    : account_info(account_info), callback(std::move(callback)) {}

TrustedVaultClientAndroid::OngoingFetchKeys::OngoingFetchKeys(
    OngoingFetchKeys&&) = default;

TrustedVaultClientAndroid::OngoingFetchKeys::~OngoingFetchKeys() = default;

TrustedVaultClientAndroid::OngoingMarkLocalKeysAsStale::
    OngoingMarkLocalKeysAsStale(base::OnceCallback<void(bool)> callback)
    : callback(std::move(callback)) {}

TrustedVaultClientAndroid::OngoingMarkLocalKeysAsStale::
    OngoingMarkLocalKeysAsStale(OngoingMarkLocalKeysAsStale&&) = default;

TrustedVaultClientAndroid::OngoingMarkLocalKeysAsStale::
    ~OngoingMarkLocalKeysAsStale() = default;

TrustedVaultClientAndroid::OngoingGetIsRecoverabilityDegraded::
    OngoingGetIsRecoverabilityDegraded(base::OnceCallback<void(bool)> callback)
    : callback(std::move(callback)) {}

TrustedVaultClientAndroid::OngoingGetIsRecoverabilityDegraded::
    OngoingGetIsRecoverabilityDegraded(OngoingGetIsRecoverabilityDegraded&&) =
        default;

TrustedVaultClientAndroid::OngoingGetIsRecoverabilityDegraded::
    ~OngoingGetIsRecoverabilityDegraded() = default;

TrustedVaultClientAndroid::OngoingAddTrustedRecoveryMethod::
    OngoingAddTrustedRecoveryMethod(base::OnceClosure callback)
    : callback(std::move(callback)) {}

TrustedVaultClientAndroid::OngoingAddTrustedRecoveryMethod::
    OngoingAddTrustedRecoveryMethod(OngoingAddTrustedRecoveryMethod&&) =
        default;

TrustedVaultClientAndroid::OngoingAddTrustedRecoveryMethod::
    ~OngoingAddTrustedRecoveryMethod() = default;

TrustedVaultClientAndroid::TrustedVaultClientAndroid(
    const GetAccountInfoByGaiaIdCallback& gaia_account_info_by_gaia_id_cb)
    : gaia_account_info_by_gaia_id_cb_(gaia_account_info_by_gaia_id_cb) {
  JNIEnv* const env = base::android::AttachCurrentThread();
  Java_TrustedVaultClient_registerNative(env, reinterpret_cast<intptr_t>(this));
}

TrustedVaultClientAndroid::~TrustedVaultClientAndroid() {
  JNIEnv* const env = base::android::AttachCurrentThread();
  Java_TrustedVaultClient_unregisterNative(env,
                                           reinterpret_cast<intptr_t>(this));
}

void TrustedVaultClientAndroid::FetchKeysCompleted(
    JNIEnv* env,
    jint request_id,
    const base::android::JavaParamRef<jstring>& gaia_id,
    const base::android::JavaParamRef<jobjectArray>& keys) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  OngoingRequest ongoing_request = GetAndUnregisterOngoingRequest(request_id);
  OngoingFetchKeys& ongoing_fetch_keys =
      absl::get<OngoingFetchKeys>(ongoing_request);

  DCHECK_EQ(ongoing_fetch_keys.account_info.gaia,
            base::android::ConvertJavaStringToUTF8(env, gaia_id))
      << "User mismatch in FetchKeys() response";

  std::vector<std::vector<uint8_t>> converted_keys;
  base::android::JavaArrayOfByteArrayToBytesVector(env, keys, &converted_keys);
  std::move(ongoing_fetch_keys.callback).Run(converted_keys);
}

void TrustedVaultClientAndroid::MarkLocalKeysAsStaleCompleted(
    JNIEnv* env,
    jint request_id,
    jboolean succeeded) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  OngoingRequest ongoing_request = GetAndUnregisterOngoingRequest(request_id);

  std::move(absl::get<OngoingMarkLocalKeysAsStale>(ongoing_request).callback)
      .Run(!!succeeded);
}

void TrustedVaultClientAndroid::GetIsRecoverabilityDegradedCompleted(
    JNIEnv* env,
    jint request_id,
    jboolean is_degraded) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  OngoingRequest ongoing_request = GetAndUnregisterOngoingRequest(request_id);

  std::move(
      absl::get<OngoingGetIsRecoverabilityDegraded>(ongoing_request).callback)
      .Run(!!is_degraded);
}

void TrustedVaultClientAndroid::AddTrustedRecoveryMethodCompleted(
    JNIEnv* env,
    jint request_id) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  OngoingRequest ongoing_request = GetAndUnregisterOngoingRequest(request_id);

  std::move(
      absl::get<OngoingAddTrustedRecoveryMethod>(ongoing_request).callback)
      .Run();
}

void TrustedVaultClientAndroid::NotifyKeysChanged(JNIEnv* env) {
  for (Observer& observer : observer_list_) {
    observer.OnTrustedVaultKeysChanged();
  }
}

void TrustedVaultClientAndroid::NotifyRecoverabilityChanged(JNIEnv* env) {
  for (Observer& observer : observer_list_) {
    observer.OnTrustedVaultRecoverabilityChanged();
  }
}

void TrustedVaultClientAndroid::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void TrustedVaultClientAndroid::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void TrustedVaultClientAndroid::FetchKeys(
    const CoreAccountInfo& account_info,
    base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> cb) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // Store for later completion when Java invokes FetchKeysCompleted().
  const RequestId request_id =
      RegisterNewOngoingRequest(OngoingFetchKeys(account_info, std::move(cb)));

  JNIEnv* const env = base::android::AttachCurrentThread();
  const base::android::ScopedJavaLocalRef<jobject> java_account_info =
      ConvertToJavaCoreAccountInfo(env, account_info);

  // Trigger the fetching keys from the implementation in Java, which will
  // eventually call FetchKeysCompleted().
  Java_TrustedVaultClient_fetchKeys(env, reinterpret_cast<intptr_t>(this),
                                    request_id, java_account_info);
}

void TrustedVaultClientAndroid::StoreKeys(
    const std::string& gaia_id,
    const std::vector<std::vector<uint8_t>>& keys,
    int last_key_version) {
  // Not supported on Android, where keys are fetched outside the browser.
  NOTREACHED_IN_MIGRATION();
}

void TrustedVaultClientAndroid::MarkLocalKeysAsStale(
    const CoreAccountInfo& account_info,
    base::OnceCallback<void(bool)> cb) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(cb);

  // Store for later completion when Java invokes
  // MarkLocalKeysAsStaleCompleted().
  const RequestId request_id =
      RegisterNewOngoingRequest(OngoingMarkLocalKeysAsStale(std::move(cb)));

  JNIEnv* const env = base::android::AttachCurrentThread();
  const base::android::ScopedJavaLocalRef<jobject> java_account_info =
      ConvertToJavaCoreAccountInfo(env, account_info);

  // The Java implementation will eventually call
  // MarkLocalKeysAsStaleCompleted().
  Java_TrustedVaultClient_markLocalKeysAsStale(
      env, reinterpret_cast<intptr_t>(this), request_id, java_account_info);
}

void TrustedVaultClientAndroid::GetIsRecoverabilityDegraded(
    const CoreAccountInfo& account_info,
    base::OnceCallback<void(bool)> cb) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(cb);

  // Store for later completion when Java invokes
  // GetIsRecoverabilityDegradedCompleted().
  const RequestId request_id = RegisterNewOngoingRequest(
      OngoingGetIsRecoverabilityDegraded(std::move(cb)));

  JNIEnv* const env = base::android::AttachCurrentThread();
  const base::android::ScopedJavaLocalRef<jobject> java_account_info =
      ConvertToJavaCoreAccountInfo(env, account_info);

  // The Java implementation will eventually call
  // MarkLocalKeysAsStaleCompleted().
  Java_TrustedVaultClient_getIsRecoverabilityDegraded(
      env, reinterpret_cast<intptr_t>(this), request_id, java_account_info);
}

void TrustedVaultClientAndroid::AddTrustedRecoveryMethod(
    const std::string& gaia_id,
    const std::vector<uint8_t>& public_key,
    int method_type_hint,
    base::OnceClosure cb) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(cb);

  const CoreAccountInfo account_info =
      gaia_account_info_by_gaia_id_cb_.Run(gaia_id);

  base::UmaHistogramBoolean(
      "Sync.TrustedVaultJavascriptAddRecoveryMethodUserKnown",
      account_info != CoreAccountInfo());

  if (account_info == CoreAccountInfo()) {
    std::move(cb).Run();
    return;
  }

  // Store for later completion when Java invokes
  // AddTrustedRecoveryMethodCompleted().
  const RequestId request_id =
      RegisterNewOngoingRequest(OngoingAddTrustedRecoveryMethod(std::move(cb)));

  JNIEnv* const env = base::android::AttachCurrentThread();
  const base::android::ScopedJavaLocalRef<jobject> java_account_info =
      ConvertToJavaCoreAccountInfo(env, account_info);

  const base::android::ScopedJavaLocalRef<jbyteArray> java_public_key =
      base::android::ToJavaByteArray(env, public_key);

  // The Java implementation will eventually call
  // AddTrustedRecoveryMethodCompleted().
  Java_TrustedVaultClient_addTrustedRecoveryMethod(
      env, reinterpret_cast<intptr_t>(this), request_id, java_account_info,
      java_public_key, method_type_hint);
}

void TrustedVaultClientAndroid::ClearLocalDataForAccount(
    const CoreAccountInfo& account_info) {
  // Not relevant for Android implementation.
}

TrustedVaultClientAndroid::RequestId
TrustedVaultClientAndroid::RegisterNewOngoingRequest(OngoingRequest request) {
  ongoing_requests_.emplace(++last_request_id_, std::move(request));
  return last_request_id_;
}

TrustedVaultClientAndroid::OngoingRequest
TrustedVaultClientAndroid::GetAndUnregisterOngoingRequest(RequestId id) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  auto it = ongoing_requests_.find(id);
  CHECK(it != ongoing_requests_.end(), base::NotFatalUntil::M130);

  OngoingRequest request = std::move(it->second);
  ongoing_requests_.erase(it);
  return request;
}

static void JNI_TrustedVaultClient_RecordKeyRetrievalTrigger(JNIEnv* env,
                                                             int trigger) {
  syncer::RecordKeyRetrievalTrigger(
      static_cast<syncer::TrustedVaultUserActionTriggerForUMA>(trigger));
}

static void JNI_TrustedVaultClient_RecordRecoverabilityDegradedFixTrigger(
    JNIEnv* env,
    int trigger) {
  syncer::RecordRecoverabilityDegradedFixTrigger(
      static_cast<syncer::TrustedVaultUserActionTriggerForUMA>(trigger));
}