camera_roll_manager_impl.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/phonehub/camera_roll_manager_impl.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "ash/components/phonehub/camera_roll_download_manager.h"
  8. #include "ash/components/phonehub/camera_roll_item.h"
  9. #include "ash/components/phonehub/camera_roll_thumbnail_decoder_impl.h"
  10. #include "ash/components/phonehub/message_receiver.h"
  11. #include "ash/components/phonehub/message_sender.h"
  12. #include "ash/components/phonehub/proto/phonehub_api.pb.h"
  13. #include "ash/components/phonehub/util/histogram_util.h"
  14. #include "ash/services/secure_channel/public/cpp/client/connection_manager.h"
  15. #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
  16. #include "base/bind.h"
  17. #include "base/memory/weak_ptr.h"
  18. #include "base/metrics/histogram_functions.h"
  19. #include "base/observer_list.h"
  20. #include "base/time/time.h"
  21. #include "third_party/abseil-cpp/absl/types/optional.h"
  22. namespace ash {
  23. namespace phonehub {
  24. namespace {
  25. constexpr int kMaxCameraRollItemCount = 4;
  26. } // namespace
  27. CameraRollManagerImpl::CameraRollManagerImpl(
  28. MessageReceiver* message_receiver,
  29. MessageSender* message_sender,
  30. multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
  31. secure_channel::ConnectionManager* connection_manager,
  32. std::unique_ptr<CameraRollDownloadManager> camera_roll_download_manager)
  33. : message_receiver_(message_receiver),
  34. message_sender_(message_sender),
  35. multidevice_setup_client_(multidevice_setup_client),
  36. connection_manager_(connection_manager),
  37. camera_roll_download_manager_(std::move(camera_roll_download_manager)),
  38. thumbnail_decoder_(std::make_unique<CameraRollThumbnailDecoderImpl>()) {
  39. message_receiver->AddObserver(this);
  40. multidevice_setup_client_->AddObserver(this);
  41. connection_manager_->AddObserver(this);
  42. }
  43. CameraRollManagerImpl::~CameraRollManagerImpl() {
  44. message_receiver_->RemoveObserver(this);
  45. multidevice_setup_client_->RemoveObserver(this);
  46. connection_manager_->RemoveObserver(this);
  47. }
  48. void CameraRollManagerImpl::DownloadItem(
  49. const proto::CameraRollItemMetadata& item_metadata) {
  50. proto::FetchCameraRollItemDataRequest request;
  51. *request.mutable_metadata() = item_metadata;
  52. message_sender_->SendFetchCameraRollItemDataRequest(request);
  53. }
  54. void CameraRollManagerImpl::OnFetchCameraRollItemDataResponseReceived(
  55. const proto::FetchCameraRollItemDataResponse& response) {
  56. if (response.file_availability() !=
  57. proto::FetchCameraRollItemDataResponse::AVAILABLE) {
  58. util::LogCameraRollDownloadResult(
  59. util::CameraRollDownloadResult::kFileNotAvailable);
  60. NotifyCameraRollDownloadError(
  61. CameraRollManager::Observer::DownloadErrorType::kGenericError,
  62. response.metadata());
  63. return;
  64. }
  65. camera_roll_download_manager_->CreatePayloadFiles(
  66. response.payload_id(), response.metadata(),
  67. base::BindOnce(&CameraRollManagerImpl::OnPayloadFilesCreated,
  68. weak_ptr_factory_.GetWeakPtr(), response));
  69. }
  70. void CameraRollManagerImpl::OnPayloadFilesCreated(
  71. const proto::FetchCameraRollItemDataResponse& response,
  72. CameraRollDownloadManager::CreatePayloadFilesResult result,
  73. absl::optional<secure_channel::mojom::PayloadFilesPtr> payload_files) {
  74. switch (result) {
  75. case CameraRollDownloadManager::CreatePayloadFilesResult::kSuccess:
  76. connection_manager_->RegisterPayloadFile(
  77. response.payload_id(), std::move(payload_files.value()),
  78. base::BindRepeating(&CameraRollManagerImpl::OnFileTransferUpdate,
  79. weak_ptr_factory_.GetWeakPtr(),
  80. response.metadata()),
  81. base::BindOnce(&CameraRollManagerImpl::OnPayloadFileRegistered,
  82. weak_ptr_factory_.GetWeakPtr(), response.metadata(),
  83. response.payload_id()));
  84. break;
  85. case CameraRollDownloadManager::CreatePayloadFilesResult::
  86. kInsufficientDiskSpace:
  87. util::LogCameraRollDownloadResult(
  88. util::CameraRollDownloadResult::kInsufficientDiskSpace);
  89. NotifyCameraRollDownloadError(
  90. CameraRollManager::Observer::DownloadErrorType::kInsufficientStorage,
  91. response.metadata());
  92. break;
  93. case CameraRollDownloadManager::CreatePayloadFilesResult::kInvalidFileName:
  94. util::LogCameraRollDownloadResult(
  95. util::CameraRollDownloadResult::kInvalidFileName);
  96. NotifyCameraRollDownloadError(
  97. CameraRollManager::Observer::DownloadErrorType::kGenericError,
  98. response.metadata());
  99. break;
  100. case CameraRollDownloadManager::CreatePayloadFilesResult::
  101. kPayloadAlreadyExists:
  102. util::LogCameraRollDownloadResult(
  103. util::CameraRollDownloadResult::kPayloadAlreadyExists);
  104. NotifyCameraRollDownloadError(
  105. CameraRollManager::Observer::DownloadErrorType::kGenericError,
  106. response.metadata());
  107. break;
  108. case CameraRollDownloadManager::CreatePayloadFilesResult::
  109. kNotUniqueFilePath:
  110. util::LogCameraRollDownloadResult(
  111. util::CameraRollDownloadResult::kNotUniqueFilePath);
  112. NotifyCameraRollDownloadError(
  113. CameraRollManager::Observer::DownloadErrorType::kGenericError,
  114. response.metadata());
  115. break;
  116. }
  117. }
  118. void CameraRollManagerImpl::OnPayloadFileRegistered(
  119. const proto::CameraRollItemMetadata& metadata,
  120. int64_t payload_id,
  121. bool success) {
  122. if (!success) {
  123. camera_roll_download_manager_->DeleteFile(payload_id);
  124. util::LogCameraRollDownloadResult(
  125. util::CameraRollDownloadResult::kTargetFileNotAccessible);
  126. NotifyCameraRollDownloadError(
  127. CameraRollManager::Observer::DownloadErrorType::kGenericError,
  128. metadata);
  129. return;
  130. }
  131. proto::InitiateCameraRollItemTransferRequest request;
  132. *request.mutable_metadata() = metadata;
  133. request.set_payload_id(payload_id);
  134. message_sender_->SendInitiateCameraRollItemTransferRequest(request);
  135. }
  136. void CameraRollManagerImpl::OnFileTransferUpdate(
  137. const proto::CameraRollItemMetadata& metadata,
  138. secure_channel::mojom::FileTransferUpdatePtr update) {
  139. switch (update->status) {
  140. case secure_channel::mojom::FileTransferStatus::kInProgress:
  141. break;
  142. case secure_channel::mojom::FileTransferStatus::kSuccess:
  143. util::LogCameraRollDownloadResult(
  144. util::CameraRollDownloadResult::kSuccess);
  145. break;
  146. case secure_channel::mojom::FileTransferStatus::kFailure:
  147. util::LogCameraRollDownloadResult(
  148. util::CameraRollDownloadResult::kTransferFailed);
  149. NotifyCameraRollDownloadError(
  150. CameraRollManager::Observer::DownloadErrorType::kNetworkConnection,
  151. metadata);
  152. break;
  153. case secure_channel::mojom::FileTransferStatus::kCanceled:
  154. util::LogCameraRollDownloadResult(
  155. util::CameraRollDownloadResult::kTransferCanceled);
  156. NotifyCameraRollDownloadError(
  157. CameraRollManager::Observer::DownloadErrorType::kNetworkConnection,
  158. metadata);
  159. break;
  160. }
  161. camera_roll_download_manager_->UpdateDownloadProgress(std::move(update));
  162. }
  163. void CameraRollManagerImpl::OnPhoneStatusSnapshotReceived(
  164. proto::PhoneStatusSnapshot phone_status_snapshot) {
  165. UpdateCameraRollAccessStateAndNotifyIfNeeded(
  166. phone_status_snapshot.properties().camera_roll_access_state());
  167. if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
  168. ClearCurrentItems();
  169. CancelPendingThumbnailRequests();
  170. return;
  171. }
  172. SendFetchCameraRollItemsRequest();
  173. }
  174. void CameraRollManagerImpl::OnPhoneStatusUpdateReceived(
  175. proto::PhoneStatusUpdate phone_status_update) {
  176. UpdateCameraRollAccessStateAndNotifyIfNeeded(
  177. phone_status_update.properties().camera_roll_access_state());
  178. if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
  179. ClearCurrentItems();
  180. CancelPendingThumbnailRequests();
  181. return;
  182. }
  183. if (phone_status_update.has_camera_roll_updates()) {
  184. SendFetchCameraRollItemsRequest();
  185. }
  186. }
  187. void CameraRollManagerImpl::OnFetchCameraRollItemsResponseReceived(
  188. const proto::FetchCameraRollItemsResponse& response) {
  189. thumbnail_decoder_->BatchDecode(
  190. response, current_items(),
  191. base::BindOnce(&CameraRollManagerImpl::OnItemThumbnailsDecoded,
  192. thumbnail_decoder_weak_ptr_factory_.GetWeakPtr()));
  193. }
  194. void CameraRollManagerImpl::SendFetchCameraRollItemsRequest() {
  195. // Clears pending thumbnail decode requests to avoid changing the current item
  196. // set after sending it with the |FetchCameraRollItemsRequest|. These pending
  197. // thumbnails will be invalidated anyway when the new response is received.
  198. CancelPendingThumbnailRequests();
  199. // Do not update the timestamp if it is already set. It means that there's an
  200. // in-progress request. We want to measure the time it takes from the first
  201. // time we request an update to when the UI is updated. This is the time the
  202. // user spends waiting.
  203. if (!fetch_items_request_start_timestamp_) {
  204. fetch_items_request_start_timestamp_ = base::TimeTicks::Now();
  205. }
  206. proto::FetchCameraRollItemsRequest request;
  207. request.set_max_item_count(kMaxCameraRollItemCount);
  208. for (const CameraRollItem& current_item : current_items()) {
  209. *request.add_current_item_metadata() = current_item.metadata();
  210. }
  211. message_sender_->SendFetchCameraRollItemsRequest(request);
  212. }
  213. void CameraRollManagerImpl::OnItemThumbnailsDecoded(
  214. CameraRollThumbnailDecoder::BatchDecodeResult result,
  215. const std::vector<CameraRollItem>& items) {
  216. if (result == CameraRollThumbnailDecoder::BatchDecodeResult::kCompleted) {
  217. if (fetch_items_request_start_timestamp_) {
  218. base::UmaHistogramMediumTimes(
  219. "PhoneHub.CameraRoll.Latency.RefreshItems",
  220. base::TimeTicks::Now() - *fetch_items_request_start_timestamp_);
  221. fetch_items_request_start_timestamp_.reset();
  222. }
  223. SetCurrentItems(items);
  224. }
  225. }
  226. void CameraRollManagerImpl::CancelPendingThumbnailRequests() {
  227. thumbnail_decoder_weak_ptr_factory_.InvalidateWeakPtrs();
  228. }
  229. bool CameraRollManagerImpl::IsCameraRollSettingEnabled() {
  230. multidevice_setup::mojom::FeatureState camera_roll_feature_state =
  231. multidevice_setup_client_->GetFeatureState(
  232. multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
  233. return camera_roll_feature_state ==
  234. multidevice_setup::mojom::FeatureState::kEnabledByUser;
  235. }
  236. void CameraRollManagerImpl::OnFeatureStatesChanged(
  237. const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
  238. feature_states_map) {
  239. if (!IsCameraRollSettingEnabled()) {
  240. // ClearCurrentItems() would also call ComputeAndUpdateUiState()
  241. ClearCurrentItems();
  242. CancelPendingThumbnailRequests();
  243. } else {
  244. ComputeAndUpdateUiState();
  245. }
  246. }
  247. void CameraRollManagerImpl::OnConnectionStatusChanged() {
  248. if (connection_manager_->GetStatus() ==
  249. secure_channel::ConnectionManager::Status::kDisconnected) {
  250. ClearCurrentItems();
  251. CancelPendingThumbnailRequests();
  252. }
  253. }
  254. void CameraRollManagerImpl::UpdateCameraRollAccessStateAndNotifyIfNeeded(
  255. const proto::CameraRollAccessState& access_state) {
  256. bool updated_storage_granted = access_state.storage_permission_granted();
  257. if (is_android_storage_granted_ != updated_storage_granted) {
  258. is_android_storage_granted_ = updated_storage_granted;
  259. util::LogCameraRollAndroidHasStorageAccessPermission(
  260. is_android_storage_granted_);
  261. ComputeAndUpdateUiState();
  262. }
  263. }
  264. void CameraRollManagerImpl::ComputeAndUpdateUiState() {
  265. if (!is_android_storage_granted_) {
  266. ui_state_ = CameraRollUiState::NO_STORAGE_PERMISSION;
  267. NotifyCameraRollViewUiStateUpdated();
  268. return;
  269. }
  270. multidevice_setup::mojom::FeatureState feature_state =
  271. multidevice_setup_client_->GetFeatureState(
  272. multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
  273. switch (feature_state) {
  274. case multidevice_setup::mojom::FeatureState::kDisabledByUser:
  275. ui_state_ = CameraRollUiState::SHOULD_HIDE;
  276. ;
  277. break;
  278. case multidevice_setup::mojom::FeatureState::kEnabledByUser:
  279. if (current_items().empty()) {
  280. ui_state_ = CameraRollUiState::SHOULD_HIDE;
  281. } else {
  282. ui_state_ = CameraRollUiState::ITEMS_VISIBLE;
  283. }
  284. break;
  285. default:
  286. ui_state_ = CameraRollUiState::SHOULD_HIDE;
  287. break;
  288. }
  289. NotifyCameraRollViewUiStateUpdated();
  290. }
  291. } // namespace phonehub
  292. } // namespace ash