| // Copyright 2012 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/ash/drive/file_system_util.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <string> |
| #include <type_traits> |
| #include <vector> |
| |
| #include "ash/constants/ash_constants.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_switches.h" |
| #include "base/command_line.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_path.h" |
| #include "base/system/sys_info.h" |
| #include "chrome/browser/ash/drive/drive_integration_service.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/policy/profile_policy_connector.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/common/chrome_paths_internal.h" |
| #include "chromeos/ash/components/login/login_state/login_state.h" |
| #include "chromeos/ash/components/network/managed_state.h" |
| #include "chromeos/ash/components/network/network_handler.h" |
| #include "chromeos/ash/components/network/network_state.h" |
| #include "chromeos/ash/components/network/network_state_handler.h" |
| #include "components/drive/drive_pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user.h" |
| #include "components/user_manager/user_manager.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| |
| using content::BrowserThread; |
| |
| namespace drive::util { |
| |
| using user_manager::User; |
| using user_manager::UserManager; |
| |
| DriveIntegrationService* GetIntegrationServiceByProfile(Profile* profile) { |
| DriveIntegrationService* service = |
| DriveIntegrationServiceFactory::FindForProfile(profile); |
| if (!service || !service->IsMounted()) { |
| return nullptr; |
| } |
| return service; |
| } |
| |
| bool IsUnderDriveMountPoint(const base::FilePath& path) { |
| std::vector<base::FilePath::StringType> components = path.GetComponents(); |
| if (components.size() < 4) { |
| return false; |
| } |
| if (components[0] != FILE_PATH_LITERAL("/")) { |
| return false; |
| } |
| if (components[1] != FILE_PATH_LITERAL("media")) { |
| return false; |
| } |
| if (components[2] != FILE_PATH_LITERAL("fuse")) { |
| return false; |
| } |
| static const base::FilePath::CharType kPrefix[] = |
| FILE_PATH_LITERAL("drivefs"); |
| if (components[3].compare(0, std::size(kPrefix) - 1, kPrefix) != 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| base::FilePath GetCacheRootPath(const Profile* const profile) { |
| base::FilePath cache_base_path; |
| chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path); |
| base::FilePath cache_root_path = |
| cache_base_path.Append(ash::kDriveCacheDirname); |
| static const base::FilePath::CharType kFileCacheVersionDir[] = |
| FILE_PATH_LITERAL("v1"); |
| return cache_root_path.Append(kFileCacheVersionDir); |
| } |
| |
| DriveAvailability CheckDriveAvailabilityForProfile( |
| const Profile* const profile) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Disable Drive for non-Gaia accounts. |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| ash::switches::kDisableGaiaServices)) { |
| return DriveAvailability::kNotAvailableForAccountType; |
| } |
| if (!ash::LoginState::IsInitialized()) { |
| return DriveAvailability::kNotAvailableForUninitialisedLoginState; |
| } |
| // Disable Drive for incognito profiles. |
| if (profile->IsOffTheRecord()) { |
| return DriveAvailability::kNotAvailableInIncognito; |
| } |
| const User* const user = ash::ProfileHelper::Get()->GetUserByProfile(profile); |
| if (!user || !user->HasGaiaAccount()) { |
| return DriveAvailability::kNotAvailableForAccountType; |
| } |
| |
| // Disable Drive if the flag has been passed and it is a test image. |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| ash::switches::kDisableDriveFsForTesting)) { |
| base::SysInfo::CrashIfChromeOSNonTestImage(); |
| return DriveAvailability::kNotAvailableForTestImage; |
| } |
| |
| return DriveAvailability::kAvailable; |
| } |
| |
| bool IsDriveAvailableForProfile(const Profile* const profile) { |
| return CheckDriveAvailabilityForProfile(profile) == |
| DriveAvailability::kAvailable; |
| } |
| |
| DriveAvailability CheckDriveEnabledAndDriveAvailabilityForProfile( |
| const Profile* const profile) { |
| // Disable Drive if preference is set. This can happen with commandline flag |
| // --disable-drive or enterprise policy, or with user settings. |
| if (profile->GetPrefs()->GetBoolean(prefs::kDisableDrive)) { |
| return DriveAvailability::kNotAvailableWhenDisableDrivePreferenceSet; |
| } |
| |
| return CheckDriveAvailabilityForProfile(profile); |
| } |
| |
| bool IsDriveEnabledForProfile(const Profile* const profile) { |
| return CheckDriveEnabledAndDriveAvailabilityForProfile(profile) == |
| DriveAvailability::kAvailable; |
| } |
| |
| bool IsDriveFsBulkPinningAvailable(const Profile* const profile) { |
| // Check the "DriveFsBulkPinning" Chrome feature. If this feature is disabled, |
| // then it probably means that the kill switch has been activated, and the |
| // bulk-pinning feature should not be available. |
| if (!base::FeatureList::IsEnabled(ash::features::kDriveFsBulkPinning)) { |
| return false; |
| } |
| |
| // Check the "drivefs.bulk_pinning.visible" boolean pref. If this pref is |
| // false, then it probably means that it has been turned down by an enterprise |
| // policy, and the bulk-pinning feature should not be available. |
| if (profile && |
| !profile->GetPrefs()->GetBoolean(prefs::kDriveFsBulkPinningVisible)) { |
| return false; |
| } |
| |
| // For Googlers, the bulk-pinning feature is available on any kind of device. |
| if (UserManager::IsInitialized()) { |
| if (const User* const user = UserManager::Get()->GetActiveUser(); |
| user && gaia::IsGoogleInternalAccountEmail( |
| user->GetAccountId().GetUserEmail())) { |
| return true; |
| } |
| } |
| |
| // For other users (non-Googlers), the bulk-pinning feature is available on |
| // suitable devices, as controlled by the |
| // "FeatureManagementDriveFsBulkPinning" Chrome feature. |
| return base::FeatureList::IsEnabled( |
| ash::features::kFeatureManagementDriveFsBulkPinning); |
| } |
| |
| bool IsDriveFsBulkPinningAvailable() { |
| return IsDriveFsBulkPinningAvailable(ProfileManager::GetActiveUserProfile()); |
| } |
| |
| bool IsOobeDrivePinningAvailable(const Profile* const profile) { |
| const bool b = IsOobeDrivePinningScreenEnabled() && |
| IsDriveFsBulkPinningAvailable(profile); |
| VLOG(1) << "IsOobeDrivePinningAvailable() returned " << b; |
| return b; |
| } |
| |
| bool IsOobeDrivePinningAvailable() { |
| return IsOobeDrivePinningAvailable(ProfileManager::GetActiveUserProfile()); |
| } |
| |
| // To ensure that the DrivePinningScreen is always available to the wizard, |
| // regardless of the current user profile, check this to add the |
| // DrivePinningScreen to the screen_manager when initializing the |
| // wizardController. |
| bool IsOobeDrivePinningScreenEnabled() { |
| return base::FeatureList::IsEnabled(ash::features::kOobeDrivePinning) && |
| ash::features::IsOobeChoobeEnabled(); |
| } |
| |
| bool IsDriveFsMirrorSyncAvailable(const Profile* const profile) { |
| return base::FeatureList::IsEnabled(ash::features::kDriveFsMirroring); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const ConnectionStatus status) { |
| switch (status) { |
| #define PRINT(s) \ |
| case ConnectionStatus::k##s: \ |
| return out << #s; |
| PRINT(NoService) |
| PRINT(NoNetwork) |
| PRINT(NotReady) |
| PRINT(Metered) |
| PRINT(Connected) |
| #undef PRINT |
| } |
| |
| return out << "ConnectionStatus(" |
| << static_cast<std::underlying_type_t<ConnectionStatus>>(status) |
| << ")"; |
| } |
| |
| // For testing. |
| static ConnectionStatus connection_status_for_testing; |
| static bool has_connection_status_for_testing = false; |
| |
| void SetDriveConnectionStatusForTesting(const ConnectionStatus status) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| VLOG(1) << "SetDriveConnectionStatusForTesting: " << status; |
| connection_status_for_testing = status; |
| has_connection_status_for_testing = true; |
| } |
| |
| ConnectionStatus GetDriveConnectionStatus(Profile* const profile) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| using enum ConnectionStatus; |
| |
| if (has_connection_status_for_testing) { |
| VLOG(1) << "GetDriveConnectionStatus: for testing: " |
| << connection_status_for_testing; |
| return connection_status_for_testing; |
| } |
| |
| if (!GetIntegrationServiceByProfile(profile)) { |
| VLOG(1) << "GetDriveConnectionStatus: no Drive integration service"; |
| return kNoService; |
| } |
| |
| if (!ash::NetworkHandler::IsInitialized()) { |
| VLOG(1) << "GetDriveConnectionStatus: no network handler"; |
| return kNoNetwork; |
| } |
| |
| ash::NetworkStateHandler* const handler = |
| ash::NetworkHandler::Get()->network_state_handler(); |
| DCHECK(handler); |
| |
| const ash::NetworkState* const network = handler->DefaultNetwork(); |
| if (!network) { |
| VLOG(1) << "GetDriveConnectionStatus: no network"; |
| return kNoNetwork; |
| } |
| |
| if (!network->IsOnline()) { |
| VLOG(1) << "GetDriveConnectionStatus: not ready: network is " |
| << network->connection_state(); |
| return kNotReady; |
| } |
| |
| using PortalState = ash::NetworkState::PortalState; |
| if (const PortalState portal_state = network->GetPortalState(); |
| portal_state != PortalState::kOnline) { |
| VLOG(1) << "GetDriveConnectionStatus: not ready: portal is " |
| << portal_state; |
| return kNotReady; |
| } |
| |
| DCHECK(profile); |
| if (profile->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular) && |
| handler->default_network_is_metered()) { |
| VLOG(1) << "GetDriveConnectionStatus: metered"; |
| return kMetered; |
| } |
| |
| VLOG(1) << "GetDriveConnectionStatus: connected"; |
| return kConnected; |
| } |
| |
| bool IsPinnableGDocMimeType(const std::string& mime_type) { |
| static const char* const kPinnableGDocMimeTypes[] = { |
| "application/vnd.google-apps.document", |
| "application/vnd.google-apps.drawing", |
| "application/vnd.google-apps.presentation", |
| "application/vnd.google-apps.spreadsheet", |
| }; |
| |
| return base::Contains(kPinnableGDocMimeTypes, mime_type); |
| } |
| |
| int64_t ComputeDriveFsContentCacheSize(const base::FilePath& path) { |
| int64_t blocks = 0; |
| |
| using base::FileEnumerator; |
| FileEnumerator it(path, true, FileEnumerator::FILES); |
| while (!it.Next().empty()) { |
| const FileEnumerator::FileInfo& info = it.GetInfo(); |
| |
| // Skip the `chunks.db*` files. |
| if (base::StartsWith(info.GetName().value(), "chunks.db")) { |
| continue; |
| } |
| |
| blocks += info.stat().st_blocks; |
| } |
| |
| const int64_t size = blocks << 9; |
| VLOG(1) << "DriveFs cache: " << (size >> 20) << " M"; |
| return size; |
| } |
| |
| } // namespace drive::util |