Skip to content

Commit

Permalink
wallpaper: download custom wallpaper from Drive
Browse files Browse the repository at this point in the history
Custom wallpaper sync previously copied file from the local mirror
on-disk. However, this file is frequently out of date with respect to
the actual file on Drive. When the wallpaper.jpg file is changed on
Drive, it may take several minutes to propagate down to the filesystem.

To reduce the potential for mismatched files, download the file directly
from Drive.

BUG=b:245611754
TEST=set wallpaper on device A, syncs to device B on next login

Change-Id: Ie7aee7092db1e7823af21185f5ad63b120b8e5b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4062411
Reviewed-by: Olesia Marukhno <olesiamarukhno@google.com>
Commit-Queue: Jeffrey Young <cowmoo@google.com>
Reviewed-by: Austin Tankiang <austinct@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084482}
  • Loading branch information
cwmoo740 authored and Chromium LUCI CQ committed Dec 16, 2022
1 parent e499719 commit ff48893
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 24 deletions.
8 changes: 8 additions & 0 deletions ash/public/cpp/wallpaper/wallpaper_drivefs_delegate.h
Expand Up @@ -6,6 +6,7 @@
#define ASH_PUBLIC_CPP_WALLPAPER_WALLPAPER_DRIVEFS_DELEGATE_H_

#include "ash/public/cpp/ash_public_export.h"
#include "ash/public/cpp/image_downloader.h"
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "components/account_id/account_id.h"
Expand All @@ -23,6 +24,13 @@ class ASH_PUBLIC_EXPORT WallpaperDriveFsDelegate {
virtual void GetWallpaperModificationTime(
const AccountId& account_id,
base::OnceCallback<void(base::Time modification_time)> callback) = 0;

// Downloads and decodes DriveFS wallpaper file. Replies with default
// constructed `gfx::ImageSkia` in case of failure, such as the file not
// existing or DriveFS error.
virtual void DownloadAndDecodeWallpaper(
const AccountId& account_id,
ImageDownloader::DownloadCallback callback) = 0;
};

} // namespace ash
Expand Down
14 changes: 10 additions & 4 deletions ash/wallpaper/wallpaper_controller_impl.cc
Expand Up @@ -3263,8 +3263,11 @@ void WallpaperControllerImpl::HandleCustomWallpaperInfoSyncedIn(
const WallpaperInfo& wallpaper_info) {
base::FilePath drivefs_path =
wallpaper_controller_client_->GetWallpaperPathFromDriveFs(account_id);
if (drivefs_path.empty())
if (drivefs_path.empty()) {
VLOG(1)
<< "Skip syncing down custom wallpaper because DriveFS is unavailable.";
return;
}

drivefs_delegate_->GetWallpaperModificationTime(
account_id,
Expand All @@ -3283,6 +3286,8 @@ void WallpaperControllerImpl::OnGetDriveFsWallpaperModificationTime(
// If the drivefs image modification time is null, watch DriveFS for the
// file being created. If the file exists but is older than synced wallpaper
// info, watch for the file being updated by the other device.
VLOG(1) << "Skip syncing custom wallpaper from DriveFS because it is not "
"found or out of date";
drive_fs_wallpaper_watcher_.Watch(
drivefs_path, base::FilePathWatcher::Type::kNonRecursive,
base::BindRepeating(&WallpaperControllerImpl::DriveFsWallpaperChanged,
Expand All @@ -3292,13 +3297,14 @@ void WallpaperControllerImpl::OnGetDriveFsWallpaperModificationTime(
base::FilePath path_in_prefs = base::FilePath(wallpaper_info.location);
std::string file_name = path_in_prefs.BaseName().value();
std::string file_path = wallpaper_info.user_file_path;
ReadAndDecodeWallpaper(

drivefs_delegate_->DownloadAndDecodeWallpaper(
account_id,
base::BindOnce(&WallpaperControllerImpl::SaveAndSetWallpaper,
weak_factory_.GetWeakPtr(), account_id,
IsEphemeralUser(account_id), file_name, file_path,
WallpaperType::kCustomized, wallpaper_info.layout,
/*show_wallpaper=*/true),
drivefs_path);
/*show_wallpaper=*/true));
}

void WallpaperControllerImpl::DriveFsWallpaperChanged(
Expand Down
20 changes: 20 additions & 0 deletions chrome/browser/ash/drive/drive_integration_service.cc
Expand Up @@ -62,6 +62,8 @@
#include "content/public/browser/network_service_instance.h"
#include "content/public/common/user_agent.h"
#include "google_apis/common/auth_service.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_constants.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
Expand Down Expand Up @@ -1465,6 +1467,24 @@ void DriveIntegrationService::ForceReSyncFile(const base::FilePath& local_path,
std::move(callback));
}

void DriveIntegrationService::GetReadOnlyAuthenticationToken(
GetReadOnlyAuthenticationTokenCallback callback) {
if (!auth_service_) {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
// This class doesn't care about browser sync consent.
const CoreAccountId& account_id =
identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);

std::vector<std::string> scopes = {
GaiaConstants::kDriveReadOnlyOAuth2Scope};
auth_service_ = std::make_unique<google_apis::AuthService>(
identity_manager, account_id, profile_->GetURLLoaderFactory(), scopes);
}

auth_service_->StartAuthentication(std::move(callback));
}

//===================== DriveIntegrationServiceFactory =======================

DriveIntegrationServiceFactory::FactoryCallback*
Expand Down
14 changes: 14 additions & 0 deletions chrome/browser/ash/drive/drive_integration_service.h
Expand Up @@ -25,6 +25,8 @@
#include "components/drive/file_errors.h"
#include "components/drive/file_system_core_util.h"
#include "components/keyed_service/core/keyed_service.h"
#include "google_apis/common/api_error_codes.h"
#include "google_apis/common/auth_service_interface.h"

class Profile;

Expand Down Expand Up @@ -111,6 +113,9 @@ class DriveIntegrationService : public KeyedService,
std::vector<drivefs::mojom::QueryItemPtr>)>;
using GetThumbnailCallback =
base::OnceCallback<void(const absl::optional<std::vector<uint8_t>>&)>;
using GetReadOnlyAuthenticationTokenCallback =
base::OnceCallback<void(google_apis::ApiErrorCode code,
const std::string& access_token)>;

// test_mount_point_name, test_cache_root and
// test_drivefs_mojo_listener_factory are used by tests to inject customized
Expand Down Expand Up @@ -277,6 +282,12 @@ class DriveIntegrationService : public KeyedService,
void ForceReSyncFile(const base::FilePath& local_path,
base::OnceClosure callback);

// Gets a read-only OAuth token that allows downloading files from the user's
// Drive. If an error occurs or the user does not have access to download
// files from Drive, `access_token` will be an empty string.
void GetReadOnlyAuthenticationToken(
GetReadOnlyAuthenticationTokenCallback callback);

private:
enum State {
NOT_INITIALIZED,
Expand Down Expand Up @@ -397,6 +408,9 @@ class DriveIntegrationService : public KeyedService,
int drivefs_consecutive_failures_count_ = 0;
bool remount_when_online_ = false;

// Used to fetch authentication and refresh tokens from Drive.
std::unique_ptr<google_apis::AuthServiceInterface> auth_service_;

base::TimeTicks mount_start_;

// Note: This should remain the last member so it'll be destroyed and
Expand Down
23 changes: 8 additions & 15 deletions chrome/browser/ash/extensions/file_manager/private_api_drive.cc
Expand Up @@ -874,22 +874,15 @@ void FileManagerPrivateInternalGetDownloadUrlFunction::OnGotDownloadUrl(
return;
}
download_url_ = std::move(download_url);
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(

drive::DriveIntegrationService* integration_service =
drive::DriveIntegrationServiceFactory::FindForProfile(
Profile::FromBrowserContext(browser_context()));
// This class doesn't care about browser sync consent.
const CoreAccountId& account_id =
identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
std::vector<std::string> scopes;
scopes.emplace_back(GaiaConstants::kDriveReadOnlyOAuth2Scope);

scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
browser_context()
->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
auth_service_ = std::make_unique<google_apis::AuthService>(
identity_manager, account_id, url_loader_factory, scopes);
auth_service_->StartAuthentication(base::BindOnce(

// Integration service was used to fetch this download url, so should still be
// present, even if DriveFS has unmounted or experienced an error.
DCHECK(integration_service);
integration_service->GetReadOnlyAuthenticationToken(base::BindOnce(
&FileManagerPrivateInternalGetDownloadUrlFunction::OnTokenFetched, this));
}

Expand Down
Expand Up @@ -20,10 +20,6 @@
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom-forward.h"
#include "components/drive/file_errors.h"

namespace google_apis {
class AuthService;
}

namespace extensions {

namespace api {
Expand Down Expand Up @@ -176,7 +172,6 @@ class FileManagerPrivateInternalGetDownloadUrlFunction

private:
GURL download_url_;
std::unique_ptr<google_apis::AuthService> auth_service_;
};

// Implements the chrome.fileManagerPrivate.notifyDriveDialogResult method.
Expand Down
107 changes: 107 additions & 0 deletions chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl.cc
Expand Up @@ -3,6 +3,8 @@
// found in the LICENSE file.

#include "chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl.h"

#include "ash/public/cpp/image_downloader.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
Expand All @@ -13,11 +15,47 @@
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "components/account_id/account_id.h"
#include "components/drive/file_errors.h"
#include "google_apis/common/api_error_codes.h"
#include "net/http/http_request_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"

namespace ash {

namespace {

constexpr net::NetworkTrafficAnnotationTag kDriveFsDownloadWallpaperTag =
net::DefineNetworkTrafficAnnotation("wallpaper_drivefs_delegate", R"(
semantics {
sender: "Wallpaper DriveFs"
description: "Download wallpaper images from Google Drive."
trigger:
"Triggered when another device owned by the same user has uploaded a "
"wallpaper image to Google Drive to sync to this device."
data: "Readonly OAuth token for Google Drive"
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"Users can disable downloading wallpapers from drive by toggling "
"Disconnect Google Drive account in chrome://os-settings/files, or "
"by turning off wallpaper sync in chrome://os-settings/osSync"
chrome_policy: {
DriveDisabled {
DriveDisabled: true
}
SyncDisabled {
SyncDisabled: true
}
WallpaperImage {
WallpaperImage: "png or jpg encoded image data"
}
}
}
)");

// Gets a pointer to `DriveIntegrationService` to interact with DriveFS. If
// DriveFS is not enabled or mounted for this `account_id`, responds with
// `nullptr`. Caller must check null safety carefully, as DriveFS can crash,
Expand Down Expand Up @@ -82,4 +120,73 @@ void WallpaperDriveFsDelegateImpl::GetWallpaperModificationTime(
.Then(std::move(callback)));
}

void WallpaperDriveFsDelegateImpl::DownloadAndDecodeWallpaper(
const AccountId& account_id,
ImageDownloader::DownloadCallback callback) {
auto* drive_integration_service = GetDriveIntegrationService(account_id);
if (!drive_integration_service) {
VLOG(1) << "Skip downloading custom wallpaper because DriveFS is not "
"available.";
std::move(callback).Run(gfx::ImageSkia());
return;
}

// `wallpaper_path` is guaranteed to be non-empty if
// `drive_integration_service` is initialized.
const base::FilePath wallpaper_path =
WallpaperControllerClientImpl::Get()->GetWallpaperPathFromDriveFs(
account_id);
DCHECK(!wallpaper_path.empty());

drive_integration_service->GetMetadata(
wallpaper_path,
base::BindOnce(&WallpaperDriveFsDelegateImpl::OnGetDownloadUrlMetadata,
weak_ptr_factory_.GetWeakPtr(), account_id,
std::move(callback)));
}

void WallpaperDriveFsDelegateImpl::OnGetDownloadUrlMetadata(
const AccountId& account_id,
ImageDownloader::DownloadCallback callback,
drive::FileError error,
drivefs::mojom::FileMetadataPtr metadata) {
if (error != drive::FileError::FILE_ERROR_OK || !metadata ||
metadata->download_url.empty()) {
VLOG(1) << "Unable to get download url for DriveFS wallpaper";
std::move(callback).Run(gfx::ImageSkia());
return;
}

auto* drive_integration_service = GetDriveIntegrationService(account_id);
if (!drive_integration_service) {
VLOG(1) << "DriveFS no longer mounted, cannot fetch authentication token";
std::move(callback).Run(gfx::ImageSkia());
return;
}

drive_integration_service->GetReadOnlyAuthenticationToken(base::BindOnce(
&WallpaperDriveFsDelegateImpl::OnGetDownloadUrlAndAuthentication,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
GURL(metadata->download_url)));
}

void WallpaperDriveFsDelegateImpl::OnGetDownloadUrlAndAuthentication(
ImageDownloader::DownloadCallback callback,
const GURL& download_url,
google_apis::ApiErrorCode error_code,
const std::string& authentication_token) {
if (error_code != google_apis::HTTP_SUCCESS || authentication_token.empty()) {
VLOG(1) << "Unable to fetch authentication token for DriveFS with error "
<< error_code;
std::move(callback).Run(gfx::ImageSkia());
return;
}
net::HttpRequestHeaders headers;
headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
"Bearer " + authentication_token);
ImageDownloader::Get()->Download(download_url, kDriveFsDownloadWallpaperTag,
std::move(headers), absl::nullopt,
std::move(callback));
}

} // namespace ash
32 changes: 32 additions & 0 deletions chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl.h
Expand Up @@ -6,9 +6,18 @@
#define CHROME_BROWSER_ASH_WALLPAPER_WALLPAPER_DRIVEFS_DELEGATE_IMPL_H_

#include "ash/public/cpp/wallpaper/wallpaper_drivefs_delegate.h"

#include <string>

#include "ash/public/cpp/image_downloader.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom-forward.h"
#include "components/account_id/account_id.h"
#include "components/drive/file_errors.h"
#include "google_apis/common/api_error_codes.h"
#include "url/gurl.h"

namespace ash {

Expand All @@ -26,6 +35,29 @@ class WallpaperDriveFsDelegateImpl : public WallpaperDriveFsDelegate {
void GetWallpaperModificationTime(
const AccountId& account_id,
base::OnceCallback<void(base::Time modification_time)> callback) override;
void DownloadAndDecodeWallpaper(
const AccountId& account_id,
ImageDownloader::DownloadCallback callback) override;

private:
// Called when DriveFS has replied with file metadata that contains a URL to
// download the wallpaper file. Actually downloading the wallpaper file still
// requires an authentication token from Drive.
void OnGetDownloadUrlMetadata(const AccountId& account_id,
ImageDownloader::DownloadCallback callback,
drive::FileError error,
drivefs::mojom::FileMetadataPtr metadata);

// Called after `OnGetDownloadUrlMetadata` and after attempting to obtain a
// Drive authentication token. Can now attempt to download the wallpaper image
// because `download_url` and `authentication_token` are both present.
void OnGetDownloadUrlAndAuthentication(
ImageDownloader::DownloadCallback callback,
const GURL& download_url,
google_apis::ApiErrorCode error_code,
const std::string& authentication_token);

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

} // namespace ash
Expand Down
1 change: 1 addition & 0 deletions tools/traffic_annotation/summary/annotations.xml
Expand Up @@ -407,4 +407,5 @@ Refer to README.md for content description and update process.
<item id="quick_start_session_auth_requester" added_in_milestone="110" content_hash_code="08353e70" os_list="chromeos" file_path="chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc" />
<item id="network_diagnostics_routines" added_in_milestone="110" content_hash_code="0007b237" os_list="chromeos" file_path="chrome/browser/ash/net/network_diagnostics/http_request_manager.cc" />
<item id="trial_group_lookup" added_in_milestone="110" content_hash_code="047aeeca" os_list="chromeos" file_path="chromeos/ash/components/trial_group/trial_group_checker.cc" />
<item id="wallpaper_drivefs_delegate" added_in_milestone="110" content_hash_code="01771f33" os_list="chromeos" file_path="chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl.cc" />
</annotations>
1 change: 1 addition & 0 deletions tools/traffic_annotation/summary/grouping.xml
Expand Up @@ -274,6 +274,7 @@ after discussions on the right group.
<annotation id="quick_start_session_auth_requester"/>
<annotation id="network_diagnostics_routines"/>
<annotation id="trial_group_lookup"/>
<annotation id="wallpaper_drivefs_delegate"/>
</sender>
</group>
<group name="Admin Features" hidden="true">
Expand Down

0 comments on commit ff48893

Please sign in to comment.