427 lines
14 KiB
C++
427 lines
14 KiB
C++
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "brave/browser/android/brave_sync_worker.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "base/android/jni_android.h"
|
|
#include "base/android/jni_string.h"
|
|
#include "base/json/json_writer.h"
|
|
#include "base/path_service.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
|
|
#include "brave/build/android/jni_headers/BraveSyncWorker_jni.h"
|
|
#include "brave/components/brave_sync/brave_sync_prefs.h"
|
|
#include "brave/components/brave_sync/crypto/crypto.h"
|
|
#include "brave/components/brave_sync/qr_code_data.h"
|
|
#include "brave/components/brave_sync/qr_code_validator.h"
|
|
#include "brave/components/brave_sync/sync_service_impl_helper.h"
|
|
#include "brave/components/brave_sync/time_limited_words.h"
|
|
#include "brave/components/sync/driver/brave_sync_service_impl.h"
|
|
|
|
#include "chrome/browser/profiles/profile.h"
|
|
#include "chrome/browser/profiles/profile_manager.h"
|
|
#include "chrome/browser/sync/device_info_sync_service_factory.h"
|
|
#include "chrome/browser/sync/sync_service_factory.h"
|
|
|
|
#include "components/sync/driver/sync_service.h"
|
|
#include "components/sync/driver/sync_user_settings.h"
|
|
#include "components/unified_consent/unified_consent_metrics.h"
|
|
|
|
#include "content/public/browser/browser_thread.h"
|
|
|
|
#include "third_party/leveldatabase/src/include/leveldb/db.h"
|
|
|
|
// TODO(alexeybarabash): consider use of java SyncServiceImpl methods:
|
|
// addSyncStateChangedListener
|
|
// removeSyncStateChangedListener
|
|
// requestStart
|
|
// requestStop
|
|
// setFirstSetupComplete
|
|
// isFirstSetupComplete
|
|
|
|
namespace {
|
|
static const size_t SEED_BYTES_COUNT = 32u;
|
|
} // namespace
|
|
|
|
namespace chrome {
|
|
namespace android {
|
|
|
|
// Keep this to clear V1 stuff on migrating
|
|
#define DB_FILE_NAME "brave_sync_db"
|
|
|
|
BraveSyncWorker::BraveSyncWorker(JNIEnv* env,
|
|
const base::android::JavaRef<jobject>& obj)
|
|
: weak_java_brave_sync_worker_(env, obj) {
|
|
Java_BraveSyncWorker_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this));
|
|
|
|
profile_ = ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
|
|
DCHECK_NE(profile_, nullptr);
|
|
}
|
|
|
|
BraveSyncWorker::~BraveSyncWorker() {}
|
|
|
|
void BraveSyncWorker::Destroy(JNIEnv* env) {
|
|
delete this;
|
|
}
|
|
|
|
static void JNI_BraveSyncWorker_DestroyV1LevelDb(JNIEnv* env) {
|
|
base::FilePath app_data_path;
|
|
base::PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path);
|
|
base::FilePath dbFilePath = app_data_path.Append(DB_FILE_NAME);
|
|
|
|
leveldb::Status status =
|
|
leveldb::DestroyDB(dbFilePath.value().c_str(), leveldb::Options());
|
|
VLOG(3) << "[BraveSync] " << __func__ << " destroy DB status is "
|
|
<< status.ToString();
|
|
}
|
|
|
|
static void JNI_BraveSyncWorker_MarkSyncV1WasEnabledAndMigrated(JNIEnv* env) {
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
Profile* profile =
|
|
ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
|
|
brave_sync::Prefs brave_sync_prefs(profile->GetPrefs());
|
|
brave_sync_prefs.SetSyncV1WasEnabled();
|
|
brave_sync_prefs.SetSyncV1Migrated(true);
|
|
VLOG(3) << "[BraveSync] " << __func__ << " done";
|
|
}
|
|
|
|
base::android::ScopedJavaLocalRef<jstring> BraveSyncWorker::GetSyncCodeWords(
|
|
JNIEnv* env) {
|
|
auto* sync_service = GetSyncService();
|
|
std::string sync_code;
|
|
if (sync_service)
|
|
sync_code = sync_service->GetOrCreateSyncCode();
|
|
|
|
CHECK(brave_sync::crypto::IsPassphraseValid(sync_code));
|
|
|
|
return base::android::ConvertUTF8ToJavaString(env, sync_code);
|
|
}
|
|
|
|
void BraveSyncWorker::SaveCodeWords(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& passphrase) {
|
|
std::string str_passphrase =
|
|
base::android::ConvertJavaStringToUTF8(passphrase);
|
|
|
|
auto* sync_service = GetSyncService();
|
|
if (!sync_service || !sync_service->SetSyncCode(str_passphrase)) {
|
|
const std::string error_msg =
|
|
sync_service
|
|
? "invalid sync code:" + str_passphrase
|
|
: "sync service is not available";
|
|
LOG(ERROR) << error_msg;
|
|
return;
|
|
}
|
|
|
|
passphrase_ = str_passphrase;
|
|
}
|
|
|
|
syncer::BraveSyncServiceImpl* BraveSyncWorker::GetSyncService() const {
|
|
return SyncServiceFactory::IsSyncAllowed(profile_)
|
|
? static_cast<syncer::BraveSyncServiceImpl*>(
|
|
SyncServiceFactory::GetForProfile(profile_))
|
|
: nullptr;
|
|
}
|
|
|
|
// Most of methods below were taken from by PeopleHandler class to
|
|
// bring the logic of enabling / disabling sync from deskop to Android
|
|
|
|
void BraveSyncWorker::RequestSync(JNIEnv* env) {
|
|
syncer::SyncService* service = SyncServiceFactory::GetForProfile(profile_);
|
|
|
|
if (service && !sync_service_observer_.IsObservingSource(service)) {
|
|
sync_service_observer_.AddObservation(service);
|
|
}
|
|
|
|
// Mark Sync as requested by the user. It might already be requested, but
|
|
// it's not if this is either the first time the user is setting up Sync, or
|
|
// Sync was set up but then was reset via the dashboard. This also pokes the
|
|
// SyncService to start up immediately, i.e. bypass deferred startup.
|
|
if (service) {
|
|
service->GetUserSettings()->SetSyncRequested(true);
|
|
}
|
|
}
|
|
|
|
void BraveSyncWorker::MarkFirstSetupComplete() {
|
|
syncer::SyncService* service = GetSyncService();
|
|
|
|
// The sync service may be nullptr if it has been just disabled by policy.
|
|
if (!service)
|
|
return;
|
|
|
|
service->GetUserSettings()->SetSyncRequested(true);
|
|
|
|
// If the first-time setup is already complete, there's nothing else to do.
|
|
if (service->GetUserSettings()->IsFirstSetupComplete())
|
|
return;
|
|
|
|
unified_consent::metrics::RecordSyncSetupDataTypesHistrogam(
|
|
service->GetUserSettings(), profile_->GetPrefs());
|
|
|
|
// We're done configuring, so notify SyncService that it is OK to start
|
|
// syncing.
|
|
service->GetUserSettings()->SetFirstSetupComplete(
|
|
syncer::SyncFirstSetupCompleteSource::ADVANCED_FLOW_CONFIRM);
|
|
}
|
|
|
|
void BraveSyncWorker::FinalizeSyncSetup(JNIEnv* env) {
|
|
MarkFirstSetupComplete();
|
|
}
|
|
|
|
bool BraveSyncWorker::IsFirstSetupComplete(JNIEnv* env) {
|
|
syncer::SyncService* sync_service = GetSyncService();
|
|
return sync_service &&
|
|
sync_service->GetUserSettings()->IsFirstSetupComplete();
|
|
}
|
|
|
|
void BraveSyncWorker::ResetSync(JNIEnv* env) {
|
|
auto* sync_service = GetSyncService();
|
|
|
|
if (!sync_service)
|
|
return;
|
|
|
|
auto* device_info_sync_service =
|
|
DeviceInfoSyncServiceFactory::GetForProfile(profile_);
|
|
brave_sync::ResetSync(sync_service, device_info_sync_service,
|
|
base::BindOnce(&BraveSyncWorker::OnResetDone,
|
|
weak_ptr_factory_.GetWeakPtr()));
|
|
}
|
|
|
|
bool BraveSyncWorker::GetSyncV1WasEnabled(JNIEnv* env) {
|
|
brave_sync::Prefs brave_sync_prefs(profile_->GetPrefs());
|
|
bool sync_v1_was_enabled = brave_sync_prefs.IsSyncV1Enabled();
|
|
return sync_v1_was_enabled;
|
|
}
|
|
|
|
bool BraveSyncWorker::GetSyncV2MigrateNoticeDismissed(JNIEnv* env) {
|
|
brave_sync::Prefs brave_sync_prefs(profile_->GetPrefs());
|
|
bool sync_v2_migration_notice_dismissed =
|
|
brave_sync_prefs.IsSyncMigrateNoticeDismissed();
|
|
return sync_v2_migration_notice_dismissed;
|
|
}
|
|
|
|
void BraveSyncWorker::SetSyncV2MigrateNoticeDismissed(
|
|
JNIEnv* env,
|
|
bool sync_v2_migration_notice_dismissed) {
|
|
brave_sync::Prefs brave_sync_prefs(profile_->GetPrefs());
|
|
brave_sync_prefs.SetDismissSyncMigrateNotice(
|
|
sync_v2_migration_notice_dismissed);
|
|
}
|
|
|
|
void BraveSyncWorker::OnResetDone() {
|
|
syncer::SyncService* sync_service = GetSyncService();
|
|
if (sync_service) {
|
|
if (sync_service_observer_.IsObservingSource(sync_service)) {
|
|
sync_service_observer_.RemoveObservation(sync_service);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BraveSyncWorker::SetEncryptionPassphrase(syncer::SyncService* service) {
|
|
DCHECK(service);
|
|
DCHECK(service->IsEngineInitialized());
|
|
DCHECK(!this->passphrase_.empty());
|
|
|
|
syncer::SyncUserSettings* sync_user_settings = service->GetUserSettings();
|
|
DCHECK(!sync_user_settings->IsPassphraseRequired());
|
|
|
|
if (sync_user_settings->IsCustomPassphraseAllowed() &&
|
|
!sync_user_settings->IsUsingExplicitPassphrase() &&
|
|
!sync_user_settings->IsTrustedVaultKeyRequired()) {
|
|
sync_user_settings->SetEncryptionPassphrase(this->passphrase_);
|
|
ProfileMetrics::LogProfileSyncInfo(
|
|
ProfileMetrics::SYNC_CREATED_NEW_PASSPHRASE);
|
|
}
|
|
}
|
|
|
|
void BraveSyncWorker::SetDecryptionPassphrase(syncer::SyncService* service) {
|
|
DCHECK(service);
|
|
DCHECK(service->IsEngineInitialized());
|
|
DCHECK(!this->passphrase_.empty());
|
|
syncer::SyncUserSettings* sync_user_settings = service->GetUserSettings();
|
|
DCHECK(sync_user_settings->IsPassphraseRequired());
|
|
|
|
if (sync_user_settings->SetDecryptionPassphrase(this->passphrase_)) {
|
|
ProfileMetrics::LogProfileSyncInfo(
|
|
ProfileMetrics::SYNC_ENTERED_EXISTING_PASSPHRASE);
|
|
}
|
|
}
|
|
|
|
void BraveSyncWorker::OnStateChanged(syncer::SyncService* service) {
|
|
// If the sync engine has shutdown for some reason, just give up
|
|
if (!service || !service->IsEngineInitialized()) {
|
|
VLOG(3) << "[BraveSync] " << __func__ << " sync engine is not initialized";
|
|
return;
|
|
}
|
|
|
|
if (this->passphrase_.empty()) {
|
|
VLOG(3) << "[BraveSync] " << __func__ << " empty passphrase";
|
|
return;
|
|
}
|
|
|
|
if (service->GetUserSettings()->IsPassphraseRequired()) {
|
|
SetDecryptionPassphrase(service);
|
|
} else {
|
|
SetEncryptionPassphrase(service);
|
|
}
|
|
}
|
|
|
|
static void JNI_BraveSyncWorker_Init(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jobject>& jcaller) {
|
|
new BraveSyncWorker(env, jcaller);
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetSeedHexFromWords(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& seed_words) {
|
|
std::string str_seed_words =
|
|
base::android::ConvertJavaStringToUTF8(seed_words);
|
|
DCHECK(!str_seed_words.empty());
|
|
|
|
std::string sync_code_hex;
|
|
std::vector<uint8_t> bytes;
|
|
if (brave_sync::crypto::PassphraseToBytes32(str_seed_words, &bytes)) {
|
|
DCHECK_EQ(bytes.size(), SEED_BYTES_COUNT);
|
|
sync_code_hex = base::HexEncode(&bytes.at(0), bytes.size());
|
|
} else {
|
|
VLOG(1) << __func__ << " PassphraseToBytes32 failed for " << str_seed_words;
|
|
}
|
|
|
|
return base::android::ConvertUTF8ToJavaString(env, sync_code_hex);
|
|
}
|
|
|
|
std::string GetWordsFromSeedHex(const std::string& str_seed_hex) {
|
|
DCHECK(!str_seed_hex.empty());
|
|
|
|
std::vector<uint8_t> bytes;
|
|
std::string sync_code_words;
|
|
if (base::HexStringToBytes(str_seed_hex, &bytes)) {
|
|
DCHECK_EQ(bytes.size(), SEED_BYTES_COUNT);
|
|
if (bytes.size(), SEED_BYTES_COUNT) {
|
|
sync_code_words = brave_sync::crypto::PassphraseFromBytes32(bytes);
|
|
if (sync_code_words.empty()) {
|
|
VLOG(1) << __func__ << " PassphraseFromBytes32 failed for "
|
|
<< str_seed_hex;
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "wrong seed bytes " << bytes.size();
|
|
}
|
|
DCHECK_NE(sync_code_words, "");
|
|
} else {
|
|
VLOG(1) << __func__ << " HexStringToBytes failed for " << str_seed_hex;
|
|
}
|
|
|
|
return sync_code_words;
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetWordsFromSeedHex(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& seed_hex) {
|
|
std::string str_seed_hex = base::android::ConvertJavaStringToUTF8(seed_hex);
|
|
std::string sync_code_words = GetWordsFromSeedHex(str_seed_hex);
|
|
return base::android::ConvertUTF8ToJavaString(env, sync_code_words);
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetQrDataJson(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& seed_hex) {
|
|
std::string str_seed_hex = base::android::ConvertJavaStringToUTF8(seed_hex);
|
|
DCHECK(!str_seed_hex.empty());
|
|
|
|
const std::string qr_code_string =
|
|
brave_sync::QrCodeData::CreateWithActualDate(str_seed_hex)->ToJson();
|
|
|
|
return base::android::ConvertUTF8ToJavaString(env, qr_code_string);
|
|
}
|
|
|
|
int JNI_BraveSyncWorker_GetQrCodeValidationResult(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& json_qr) {
|
|
std::string str_json_qr = base::android::ConvertJavaStringToUTF8(json_qr);
|
|
DCHECK(!str_json_qr.empty());
|
|
return static_cast<int>(
|
|
brave_sync::QrCodeDataValidator::ValidateQrDataJson(str_json_qr));
|
|
}
|
|
|
|
int JNI_BraveSyncWorker_GetWordsValidationResult(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& time_limited_words) {
|
|
std::string str_time_limited_words =
|
|
base::android::ConvertJavaStringToUTF8(time_limited_words);
|
|
DCHECK(!str_time_limited_words.empty());
|
|
|
|
auto pure_words_with_status =
|
|
brave_sync::TimeLimitedWords::Parse(str_time_limited_words);
|
|
|
|
if (pure_words_with_status.has_value()) {
|
|
using ValidationStatus = brave_sync::TimeLimitedWords::ValidationStatus;
|
|
return static_cast<int>(ValidationStatus::kValid);
|
|
}
|
|
return static_cast<int>(pure_words_with_status.error());
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetPureWordsFromTimeLimited(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& time_limited_words) {
|
|
std::string str_time_limited_words =
|
|
base::android::ConvertJavaStringToUTF8(time_limited_words);
|
|
DCHECK(!str_time_limited_words.empty());
|
|
|
|
auto pure_words_with_status =
|
|
brave_sync::TimeLimitedWords::Parse(str_time_limited_words);
|
|
DCHECK(pure_words_with_status.has_value());
|
|
|
|
return base::android::ConvertUTF8ToJavaString(env,
|
|
pure_words_with_status.value());
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetTimeLimitedWordsFromPure(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& pure_words) {
|
|
std::string str_pure_words =
|
|
base::android::ConvertJavaStringToUTF8(pure_words);
|
|
DCHECK(!str_pure_words.empty());
|
|
|
|
auto time_limited_words =
|
|
brave_sync::TimeLimitedWords::GenerateForNow(str_pure_words);
|
|
|
|
DCHECK(time_limited_words.has_value());
|
|
return base::android::ConvertUTF8ToJavaString(env,
|
|
time_limited_words.value());
|
|
}
|
|
|
|
static base::android::ScopedJavaLocalRef<jstring>
|
|
JNI_BraveSyncWorker_GetSeedHexFromQrJson(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jstring>& json_qr) {
|
|
std::string str_json_qr = base::android::ConvertJavaStringToUTF8(json_qr);
|
|
DCHECK(!str_json_qr.empty());
|
|
|
|
auto qr_data = brave_sync::QrCodeData::FromJson(str_json_qr);
|
|
|
|
std::string result;
|
|
if (qr_data) {
|
|
result = qr_data->sync_code_hex;
|
|
} else {
|
|
result = str_json_qr;
|
|
}
|
|
|
|
DCHECK(!GetWordsFromSeedHex(result).empty());
|
|
|
|
return base::android::ConvertUTF8ToJavaString(env, result);
|
|
}
|
|
|
|
} // namespace android
|
|
} // namespace chrome
|