Files
nearby/sharing/nearby_sharing_settings.cc
T
2026-07-23 14:55:42 -07:00

431 lines
16 KiB
C++

// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/nearby_sharing_settings.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
#include "internal/platform/clock.h"
#include "internal/platform/implementation/device_info.h"
#include "internal/platform/task_runner.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/common/nearby_share_prefs.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/public/logging.h"
#include "sharing/internal/public/pref_names.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/thread_timer.h"
namespace nearby {
namespace sharing {
namespace {
using ::location::nearby::proto::sharing::ShowNotificationStatus;
using ::nearby::sharing::api::PreferenceManager;
using ::nearby::sharing::proto::DataUsage;
using ::nearby::sharing::proto::DeviceVisibility;
using ::nearby::sharing::proto::FastInitiationNotificationState;
using ::nearby::sharing::sync::SyncBindingPrefs;
constexpr absl::string_view kPreferencesObserverName =
"nearby-sharing-settings";
constexpr int kMaxVisibilityExpirationSeconds =
prefs::kDefaultMaxVisibilityExpirationSeconds;
ShowNotificationStatus GetNotificationStatus(
FastInitiationNotificationState state) {
switch (state) {
case FastInitiationNotificationState::ENABLED_FAST_INIT:
return ShowNotificationStatus::SHOW;
case FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT:
case FastInitiationNotificationState::DISABLED_BY_FEATURE_FAST_INIT:
return ShowNotificationStatus::NOT_SHOW;
default:
return ShowNotificationStatus::UNKNOWN_SHOW_NOTIFICATION_STATUS;
}
}
} // namespace
NearbyShareSettings::NearbyShareSettings(
TaskRunner* absl_nonnull task_runner, nearby::Clock* absl_nonnull clock,
nearby::api::DeviceInfo& device_info, PreferenceManager& preference_manager,
NearbyShareLocalDeviceDataManager* local_device_data_manager,
analytics::AnalyticsRecorder* analytics_recorder)
: task_runner_(*task_runner),
clock_(*clock),
device_info_(device_info),
preference_manager_(preference_manager),
local_device_data_manager_(local_device_data_manager),
analytics_recorder_(analytics_recorder),
fallback_visibility_(prefs::kDefaultFallbackVisibility) {
is_desctructing_ = std::make_shared<bool>(false);
RestoreFallbackVisibility();
preference_manager_.AddObserver(
kPreferencesObserverName,
[this, desctructing =
std::weak_ptr<bool>(is_desctructing_)](absl::string_view key) {
std::shared_ptr<bool> is_desctructing = desctructing.lock();
if (is_desctructing == nullptr || *is_desctructing) {
LOG(WARNING) << ": Ignore the preferences change callback.";
return;
}
OnPreferenceChanged(key);
});
local_device_data_manager_->AddObserver(this);
}
NearbyShareSettings::~NearbyShareSettings() {
absl::MutexLock lock(mutex_);
is_desctructing_ = nullptr;
preference_manager_.RemoveObserver(kPreferencesObserverName);
local_device_data_manager_->RemoveObserver(this);
}
FastInitiationNotificationState
NearbyShareSettings::GetFastInitiationNotificationState() const {
return static_cast<FastInitiationNotificationState>(
preference_manager_.GetInteger(
PrefNames::kFastInitiationNotificationState,
static_cast<int>(
FastInitiationNotificationState::ENABLED_FAST_INIT)));
}
void NearbyShareSettings::SetIsFastInitiationHardwareSupported(
bool is_supported) {
{
absl::MutexLock lock(mutex_);
// If the new value is the same as the old value, don't notify observers.
if (is_fast_initiation_hardware_supported_ == is_supported) {
return;
}
is_fast_initiation_hardware_supported_ = is_supported;
}
for (Observer* observer : observers_set_.GetObservers()) {
observer->OnIsFastInitiationHardwareSupportedChanged(is_supported);
}
}
std::string NearbyShareSettings::GetDeviceName() const {
return local_device_data_manager_->GetDeviceName();
}
DataUsage NearbyShareSettings::GetDataUsage() const {
return static_cast<DataUsage>(
preference_manager_.GetInteger(PrefNames::kDataUsage, 0));
}
void NearbyShareSettings::StartVisibilityTimer(
absl::Duration expiration) {
LOG(INFO) << __func__
<< ": start visibility timer. expiration=" << expiration;
visibility_expiration_timer_ = std::make_unique<ThreadTimer>(
task_runner_, "nearby_share_settings_visibility_timer",
expiration, [this]() {
LOG(INFO) << __func__ << ": visibility timer expired.";
proto::DeviceVisibility visibility;
{
absl::MutexLock lock(mutex_);
visibility = fallback_visibility_;
visibility_expiration_timer_.reset();
}
SetVisibility(visibility);
});
}
void NearbyShareSettings::RestoreFallbackVisibility() {
int64_t expiration_seconds = preference_manager_.GetInteger(
PrefNames::kVisibilityExpirationSeconds, 0);
int64_t fallback_visibility = preference_manager_.GetInteger(
PrefNames::kFallbackVisibility,
static_cast<int>(prefs::kDefaultFallbackVisibility));
fallback_visibility_ = static_cast<DeviceVisibility>(fallback_visibility);
int64_t now_seconds = absl::ToUnixSeconds(clock_.Now());
int64_t remaining_seconds = expiration_seconds - now_seconds;
int64_t diff = kMaxVisibilityExpirationSeconds - remaining_seconds;
LOG(INFO) << __func__ << ": diff=" << diff << ", now=" << now_seconds
<< ", expiration=" << expiration_seconds
<< ", max=" << kMaxVisibilityExpirationSeconds;
if (remaining_seconds > 0 &&
remaining_seconds <= kMaxVisibilityExpirationSeconds) { // Not expired
StartVisibilityTimer(absl::Seconds(remaining_seconds));
} else if (expiration_seconds != 0) { // Expired.
LOG(INFO) << __func__
<< ": timer is already expired. Restore fallback visibility.";
SetVisibility(static_cast<DeviceVisibility>(fallback_visibility));
} else {
LOG(INFO) << __func__ << ": No running fallback Visibility.";
}
}
std::string NearbyShareSettings::GetCustomSavePath() const {
std::string custom_save_path = preference_manager_.GetString(
PrefNames::kCustomSavePath, device_info_.GetDownloadPath().ToString());
if (Files::IsAbsolutePath(FilePath(custom_save_path))) {
return custom_save_path;
}
return device_info_.GetDownloadPath().ToString();
}
SyncBindingPrefs NearbyShareSettings::GetSyncBindingPrefs() const {
return preference_manager_.GetSyncBindingValue().value_or(
SyncBindingPrefs());
}
void NearbyShareSettings::SetSyncBindingPrefs(
const SyncBindingPrefs& prefs) {
preference_manager_.SetSyncBindingValue(prefs);
}
bool NearbyShareSettings::IsDisabledByPolicy() const { return false; }
void NearbyShareSettings::AddSettingsObserver(Observer* observer) {
observers_set_.AddObserver(observer);
}
void NearbyShareSettings::RemoveSettingsObserver(Observer* observer) {
observers_set_.RemoveObserver(observer);
}
void NearbyShareSettings::SetFastInitiationNotificationState(
FastInitiationNotificationState state) {
if (analytics_recorder_ != nullptr) {
analytics_recorder_->NewToggleShowNotification(
GetNotificationStatus(GetFastInitiationNotificationState()),
GetNotificationStatus(state));
}
preference_manager_.SetInteger(PrefNames::kFastInitiationNotificationState,
static_cast<int>(state));
}
void NearbyShareSettings::SetDeviceName(
absl::string_view device_name,
std::function<void(DeviceNameValidationResult)> callback) {
if (analytics_recorder_ != nullptr) {
analytics_recorder_->NewSetDeviceName(device_name.size());
}
std::move(callback)(local_device_data_manager_->SetDeviceName(device_name));
}
void NearbyShareSettings::SetDataUsage(DataUsage data_usage) {
if (analytics_recorder_ != nullptr) {
analytics_recorder_->NewSetDataUsage(GetDataUsage(), data_usage);
}
preference_manager_.SetInteger(PrefNames::kDataUsage,
static_cast<int>(data_usage));
}
DeviceVisibility NearbyShareSettings::GetVisibility() const {
DeviceVisibility visibility =
static_cast<DeviceVisibility>(preference_manager_.GetInteger(
PrefNames::kVisibility, static_cast<int>(prefs::kDefaultVisibility)));
if (visibility == DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS) {
// Set the visibility to self share if it's only visible to selected
// contacts, as part of QuickShare rebrand work.
visibility = DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE;
}
return visibility;
}
void NearbyShareSettings::SetVisibility(DeviceVisibility visibility,
absl::Duration expiration) {
absl::MutexLock lock(mutex_);
if (visibility == DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS) {
// This should really be an error, but this function does not return errors,
// so change it to self share as in GetVisibility().
visibility = DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE;
}
DeviceVisibility last_visibility = GetVisibility();
absl::Duration max_expiration =
absl::Seconds(kMaxVisibilityExpirationSeconds);
if (expiration > max_expiration) {
LOG(WARNING) << "Set visbility expiration is too long. visibility="
<< static_cast<int>(visibility)
<< ", expiration=" << expiration;
expiration = max_expiration;
}
if (analytics_recorder_ != nullptr) {
analytics_recorder_->NewSetVisibility(
last_visibility, visibility, absl::ToInt64Milliseconds(expiration));
}
VLOG(1) << __func__
<< ": set visibility. visibility=" << static_cast<int>(visibility)
<< ", expiration=" << expiration;
visibility_expiration_timer_.reset();
SetFallbackVisibility(last_visibility);
absl::Time now = clock_.Now();
if (expiration != absl::ZeroDuration()) {
VLOG(1) << __func__ << ": temporary visibility timer starts.";
absl::Time fallback_visibility_timestamp = now + expiration;
preference_manager_.SetInteger(
PrefNames::kVisibilityExpirationSeconds,
absl::ToUnixSeconds(fallback_visibility_timestamp));
StartVisibilityTimer(expiration);
} else {
preference_manager_.SetInteger(PrefNames::kVisibilityExpirationSeconds, 0);
}
last_visibility_timestamp_ = now;
last_visibility_ = last_visibility;
preference_manager_.SetInteger(PrefNames::kVisibility,
static_cast<int>(visibility));
}
absl::Time NearbyShareSettings::GetLastVisibilityTimestamp() const {
absl::MutexLock lock(mutex_);
return last_visibility_timestamp_;
}
proto::DeviceVisibility NearbyShareSettings::GetLastVisibility() const {
absl::MutexLock lock(mutex_);
return static_cast<proto::DeviceVisibility>(last_visibility_);
}
NearbyShareSettings::FallbackVisibilityInfo
NearbyShareSettings::GetRawFallbackVisibility() const {
return {
.visibility = fallback_visibility_,
.fallback_time = absl::FromUnixSeconds(preference_manager_.GetInteger(
PrefNames::kVisibilityExpirationSeconds, 0))
};
}
NearbyShareSettings::FallbackVisibilityInfo
NearbyShareSettings::GetFallbackVisibility() const {
absl::MutexLock lock(mutex_);
FallbackVisibilityInfo result{
.visibility = DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED,
.fallback_time = absl::UnixEpoch(),
};
// Check if visibility is temporary.
if (visibility_expiration_timer_ != nullptr &&
visibility_expiration_timer_->IsRunning()) {
result = GetRawFallbackVisibility();
}
VLOG(1) << __func__ << ": get fallback visibility "
<< static_cast<int>(result.visibility)
<< " expiration: " << result.fallback_time;
return result;
}
void NearbyShareSettings::SetFallbackVisibility(DeviceVisibility visibility) {
VLOG(1) << __func__ << ": set fallback visibility. visibility="
<< static_cast<int>(visibility);
if (visibility == DeviceVisibility::DEVICE_VISIBILITY_EVERYONE) {
VLOG(1) << __func__ << ": visibility is everyone. Skip.";
return;
}
fallback_visibility_ = visibility;
preference_manager_.SetInteger(PrefNames::kFallbackVisibility,
static_cast<int>(visibility));
}
void NearbyShareSettings::SetCustomSavePathAsync(
absl::string_view save_path, const std::function<void()>& callback) {
if (!Files::IsAbsolutePath(FilePath(save_path))) {
callback();
return;
}
absl::MutexLock lock(mutex_);
preference_manager_.SetString(PrefNames::kCustomSavePath, save_path);
callback();
}
void NearbyShareSettings::OnPreferenceChanged(absl::string_view key) {
if (key == PrefNames::kFastInitiationNotificationState) {
NotifyAllObservers(key, Observer::Data(static_cast<int64_t>(
GetFastInitiationNotificationState())));
} else if (key == PrefNames::kVisibility) {
NotifyAllObservers(key,
Observer::Data(static_cast<int64_t>(GetVisibility())));
} else if (key == PrefNames::kDataUsage) {
NotifyAllObservers(key,
Observer::Data(static_cast<int64_t>(GetDataUsage())));
} else if (key == PrefNames::kCustomSavePath) {
NotifyAllObservers(key, Observer::Data(GetCustomSavePath()));
} else {
// Not a monitored key.
return;
}
}
void NearbyShareSettings::OnLocalDeviceDataChanged(bool did_device_name_change,
bool did_full_name_change,
bool did_icon_url_change) {
if (!did_device_name_change) return;
std::string device_name = GetDeviceName();
NotifyAllObservers(PrefNames::kDeviceName, Observer::Data(device_name));
}
void NearbyShareSettings::NotifyAllObservers(absl::string_view key,
Observer::Data value) {
for (Observer* observer : observers_set_.GetObservers()) {
observer->OnSettingChanged(key, value);
}
}
bool NearbyShareSettings::GetIsAnalyticsEnabled() const {
return preference_manager_.GetBoolean(PrefNames::kIsAnalyticsEnabled, true);
}
void NearbyShareSettings::SetIsAnalyticsEnabled(bool is_analytics_enabled) {
preference_manager_.SetBoolean(PrefNames::kIsAnalyticsEnabled,
is_analytics_enabled);
}
std::string NearbyShareSettings::Dump() const {
std::stringstream sstream;
sstream << "Nearby Share Settings" << std::endl;
sstream << " Device name: " << GetDeviceName() << std::endl;
sstream << " Visibility: " << DeviceVisibility_Name(GetVisibility())
<< std::endl;
sstream << " FastInitiationNotification: "
<< FastInitiationNotificationState_Name(
GetFastInitiationNotificationState())
<< std::endl;
sstream << " DataUsage: " << DataUsage_Name(GetDataUsage()) << std::endl;
sstream << " Last Visibility: " << DeviceVisibility_Name(GetLastVisibility())
<< std::endl;
return sstream.str();
}
bool NearbyShareSettings::is_fast_initiation_hardware_supported() {
absl::MutexLock lock(mutex_);
return is_fast_initiation_hardware_supported_;
}
} // namespace sharing
} // namespace nearby