Commit 5d7e47f4 authored by Wei Lee's avatar Wei Lee Committed by Chromium LUCI CQ

CCA: Sends broadcast to ARC when capturing new photo/video

It is a requirement on Android R to pass the CtsVerifier Camera
Intents test.

Bug: b/173559007
Test: Pass CtsVerifier Camera Intents test.

Change-Id: If2e9bc084af62ce634242c1f45eb7e339a15d452
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2560436
Commit-Queue: Wei Lee <wtlee@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarShik Chen <shik@chromium.org>
Reviewed-by: default avatarDavid Jacobo <djacobo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836520}
parent 91f69d9a
...@@ -110,22 +110,17 @@ bool ChromeCameraAppUIDelegate::IsMetricsAndCrashReportingEnabled() { ...@@ -110,22 +110,17 @@ bool ChromeCameraAppUIDelegate::IsMetricsAndCrashReportingEnabled() {
} }
void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) { void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) {
// Check to avoid directory traversal attack. base::FilePath path = GetFilePathByName(name);
base::FilePath name_component(name); if (path.empty()) {
if (name_component.ReferencesParent())
return; return;
}
Profile* profile = Profile::FromWebUI(web_ui_);
base::FilePath path = file_manager::util::GetMyFilesFolderForProfile(profile)
.Append("Camera")
.Append(name_component);
auto&& file_paths = std::vector<base::FilePath>({path}); auto&& file_paths = std::vector<base::FilePath>({path});
apps::mojom::FilePathsPtr launch_files = apps::mojom::FilePathsPtr launch_files =
apps::mojom::FilePaths::New(file_paths); apps::mojom::FilePaths::New(file_paths);
apps::AppServiceProxy* proxy = apps::AppServiceProxy* proxy =
apps::AppServiceProxyFactory::GetForProfile(profile); apps::AppServiceProxyFactory::GetForProfile(Profile::FromWebUI(web_ui_));
proxy->LaunchAppWithFiles( proxy->LaunchAppWithFiles(
web_app::kMediaAppId, web_app::kMediaAppId,
apps::mojom::LaunchContainer::kLaunchContainerWindow, apps::mojom::LaunchContainer::kLaunchContainerWindow,
...@@ -149,3 +144,32 @@ void ChromeCameraAppUIDelegate::OpenFeedbackDialog( ...@@ -149,3 +144,32 @@ void ChromeCameraAppUIDelegate::OpenFeedbackDialog(
"chromeos-camera-app" /* category_tag */, "chromeos-camera-app" /* category_tag */,
std::string() /* extra_diagnostics */); std::string() /* extra_diagnostics */);
} }
std::string ChromeCameraAppUIDelegate::GetFilePathInArcByName(
const std::string& name) {
base::FilePath path = GetFilePathByName(name);
if (path.empty()) {
return std::string();
}
GURL arc_url_out;
if (!file_manager::util::ConvertPathToArcUrl(path, &arc_url_out) ||
!arc_url_out.is_valid()) {
return std::string();
}
return arc_url_out.spec();
}
base::FilePath ChromeCameraAppUIDelegate::GetFilePathByName(
const std::string& name) {
// Check to avoid directory traversal attack.
base::FilePath name_component(name);
if (name_component.ReferencesParent())
return base::FilePath();
Profile* profile = Profile::FromWebUI(web_ui_);
return file_manager::util::GetMyFilesFolderForProfile(profile)
.Append("Camera")
.Append(name_component);
}
...@@ -73,8 +73,11 @@ class ChromeCameraAppUIDelegate : public CameraAppUIDelegate { ...@@ -73,8 +73,11 @@ class ChromeCameraAppUIDelegate : public CameraAppUIDelegate {
bool IsMetricsAndCrashReportingEnabled() override; bool IsMetricsAndCrashReportingEnabled() override;
void OpenFileInGallery(const std::string& name) override; void OpenFileInGallery(const std::string& name) override;
void OpenFeedbackDialog(const std::string& placeholder) override; void OpenFeedbackDialog(const std::string& placeholder) override;
std::string GetFilePathInArcByName(const std::string& name) override;
private: private:
base::FilePath GetFilePathByName(const std::string& name);
content::WebUI* web_ui_; // Owns |this|. content::WebUI* web_ui_; // Owns |this|.
}; };
......
...@@ -142,4 +142,9 @@ interface CameraAppHelper { ...@@ -142,4 +142,9 @@ interface CameraAppHelper {
// Gets the controller to control and monitor the window state of app. // Gets the controller to control and monitor the window state of app.
GetWindowStateController() GetWindowStateController()
=> (pending_remote<WindowStateController> controller); => (pending_remote<WindowStateController> controller);
// Sends broadcast when a picture/video is captured. |is_video| is true if a
// video is captured and false for a picture. |name| is the file name which
// will be used to generate the URI for the broadcast.
SendNewCaptureBroadcast(bool is_video, string name);
}; };
...@@ -57,9 +57,11 @@ base::Optional<uint32_t> ParseIntentIdFromUrl(const GURL& url) { ...@@ -57,9 +57,11 @@ base::Optional<uint32_t> ParseIntentIdFromUrl(const GURL& url) {
CameraAppHelperImpl::CameraAppHelperImpl( CameraAppHelperImpl::CameraAppHelperImpl(
chromeos::CameraAppUI* camera_app_ui, chromeos::CameraAppUI* camera_app_ui,
CameraResultCallback camera_result_callback, CameraResultCallback camera_result_callback,
SendBroadcastCallback send_broadcast_callback,
aura::Window* window) aura::Window* window)
: camera_app_ui_(camera_app_ui), : camera_app_ui_(camera_app_ui),
camera_result_callback_(std::move(camera_result_callback)), camera_result_callback_(std::move(camera_result_callback)),
send_broadcast_callback_(std::move(send_broadcast_callback)),
has_external_screen_(HasExternalScreen()), has_external_screen_(HasExternalScreen()),
pending_intent_id_(base::nullopt), pending_intent_id_(base::nullopt),
window_(window) { window_(window) {
...@@ -191,6 +193,18 @@ void CameraAppHelperImpl::GetWindowStateController( ...@@ -191,6 +193,18 @@ void CameraAppHelperImpl::GetWindowStateController(
std::move(callback).Run(std::move(controller_remote)); std::move(callback).Run(std::move(controller_remote));
} }
void CameraAppHelperImpl::SendNewCaptureBroadcast(bool is_video,
const std::string& name) {
auto file_path = camera_app_ui_->delegate()->GetFilePathInArcByName(name);
if (file_path.empty()) {
LOG(ERROR) << "Drop the broadcast request due to invalid file path in ARC "
"generated by the file name: "
<< name;
return;
}
send_broadcast_callback_.Run(is_video, file_path);
}
void CameraAppHelperImpl::OnTabletModeStarted() { void CameraAppHelperImpl::OnTabletModeStarted() {
if (tablet_mode_monitor_.is_bound()) if (tablet_mode_monitor_.is_bound())
tablet_mode_monitor_->Update(true); tablet_mode_monitor_->Update(true);
......
...@@ -31,6 +31,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver, ...@@ -31,6 +31,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
arc::mojom::CameraIntentAction, arc::mojom::CameraIntentAction,
const std::vector<uint8_t>&, const std::vector<uint8_t>&,
HandleCameraResultCallback)>; HandleCameraResultCallback)>;
using SendBroadcastCallback =
base::RepeatingCallback<void(bool, std::string)>;
using TabletModeMonitor = mojom::TabletModeMonitor; using TabletModeMonitor = mojom::TabletModeMonitor;
using ScreenStateMonitor = mojom::ScreenStateMonitor; using ScreenStateMonitor = mojom::ScreenStateMonitor;
using ExternalScreenMonitor = mojom::ExternalScreenMonitor; using ExternalScreenMonitor = mojom::ExternalScreenMonitor;
...@@ -38,6 +40,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver, ...@@ -38,6 +40,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
CameraAppHelperImpl(chromeos::CameraAppUI* camera_app_ui, CameraAppHelperImpl(chromeos::CameraAppUI* camera_app_ui,
CameraResultCallback camera_result_callback, CameraResultCallback camera_result_callback,
SendBroadcastCallback send_broadcast_callback,
aura::Window* window); aura::Window* window);
~CameraAppHelperImpl() override; ~CameraAppHelperImpl() override;
void Bind(mojo::PendingReceiver<mojom::CameraAppHelper> receiver); void Bind(mojo::PendingReceiver<mojom::CameraAppHelper> receiver);
...@@ -66,6 +69,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver, ...@@ -66,6 +69,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
SetCameraUsageMonitorCallback callback) override; SetCameraUsageMonitorCallback callback) override;
void GetWindowStateController( void GetWindowStateController(
GetWindowStateControllerCallback callback) override; GetWindowStateControllerCallback callback) override;
void SendNewCaptureBroadcast(bool is_video, const std::string& name) override;
private: private:
void CheckExternalScreenState(); void CheckExternalScreenState();
...@@ -89,6 +93,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver, ...@@ -89,6 +93,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
CameraResultCallback camera_result_callback_; CameraResultCallback camera_result_callback_;
SendBroadcastCallback send_broadcast_callback_;
bool has_external_screen_; bool has_external_screen_;
base::Optional<uint32_t> pending_intent_id_; base::Optional<uint32_t> pending_intent_id_;
......
...@@ -122,6 +122,14 @@ void HandleCameraResult( ...@@ -122,6 +122,14 @@ void HandleCameraResult(
std::move(callback)); std::move(callback));
} }
void SendNewCaptureBroadcast(content::BrowserContext* context,
bool is_video,
std::string file_path) {
auto* intent_helper =
arc::ArcIntentHelperBridge::GetForBrowserContext(context);
intent_helper->SendNewCaptureBroadcast(is_video, file_path);
}
std::unique_ptr<media::CameraAppDeviceProviderImpl> std::unique_ptr<media::CameraAppDeviceProviderImpl>
CreateCameraAppDeviceProvider(const url::Origin& security_origin, CreateCameraAppDeviceProvider(const url::Origin& security_origin,
content::BrowserContext* context) { content::BrowserContext* context) {
...@@ -149,9 +157,12 @@ std::unique_ptr<chromeos_camera::CameraAppHelperImpl> CreateCameraAppHelper( ...@@ -149,9 +157,12 @@ std::unique_ptr<chromeos_camera::CameraAppHelperImpl> CreateCameraAppHelper(
DCHECK_NE(window, nullptr); DCHECK_NE(window, nullptr);
auto handle_result_callback = auto handle_result_callback =
base::BindRepeating(&HandleCameraResult, browser_context); base::BindRepeating(&HandleCameraResult, browser_context);
auto send_broadcast_callback =
base::BindRepeating(&SendNewCaptureBroadcast, browser_context);
return std::make_unique<chromeos_camera::CameraAppHelperImpl>( return std::make_unique<chromeos_camera::CameraAppHelperImpl>(
camera_app_ui, std::move(handle_result_callback), window); camera_app_ui, std::move(handle_result_callback),
std::move(send_broadcast_callback), window);
} }
} // namespace } // namespace
......
...@@ -34,6 +34,9 @@ class CameraAppUIDelegate { ...@@ -34,6 +34,9 @@ class CameraAppUIDelegate {
// Opens the native chrome feedback dialog scoped to chrome://camera-app and // Opens the native chrome feedback dialog scoped to chrome://camera-app and
// show |placeholder| in the description field. // show |placeholder| in the description field.
virtual void OpenFeedbackDialog(const std::string& placeholder) = 0; virtual void OpenFeedbackDialog(const std::string& placeholder) = 0;
// Gets the file path in ARC file system by given file |name|.
virtual std::string GetFilePathInArcByName(const std::string& name) = 0;
}; };
#endif // CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_DELEGATE_H_ #endif // CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_DELEGATE_H_
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import {ResultSaver} from './models/result_saver.js'; import {ResultSaver} from './models/result_saver.js';
import {VideoSaver} from './models/video_saver.js'; import {VideoSaver} from './models/video_saver.js';
import {ChromeHelper} from './mojo/chrome_helper.js';
import * as util from './util.js'; import * as util from './util.js';
/** /**
...@@ -181,6 +182,9 @@ export class GalleryButton { ...@@ -181,6 +182,9 @@ export class GalleryButton {
*/ */
async savePhoto(blob, name) { async savePhoto(blob, name) {
const file = await filesystem.saveBlob(blob, name); const file = await filesystem.saveBlob(blob, name);
ChromeHelper.getInstance().sendNewCaptureBroadcast(
{isVideo: false, name: file.name});
await this.updateCover_(file); await this.updateCover_(file);
} }
...@@ -198,6 +202,9 @@ export class GalleryButton { ...@@ -198,6 +202,9 @@ export class GalleryButton {
async finishSaveVideo(video) { async finishSaveVideo(video) {
const file = await video.endWrite(); const file = await video.endWrite();
assert(file !== null); assert(file !== null);
ChromeHelper.getInstance().sendNewCaptureBroadcast(
{isVideo: true, name: file.name});
await this.updateCover_(file); await this.updateCover_(file);
} }
} }
...@@ -252,6 +252,15 @@ export class ChromeHelper { ...@@ -252,6 +252,15 @@ export class ChromeHelper {
return isEnabled; return isEnabled;
} }
/**
* Sends the broadcast to ARC to notify the new photo/video is captured.
* @param {{isVideo: boolean, name: string}} info
* @return {!Promise}
*/
async sendNewCaptureBroadcast({isVideo, name}) {
this.remote_.sendNewCaptureBroadcast(isVideo, name);
}
/** /**
* Creates a new instance of ChromeHelper if it is not set. Returns the * Creates a new instance of ChromeHelper if it is not set. Returns the
* exist instance. * exist instance.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/public/cpp/new_window_delegate.h" #include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/wallpaper_controller.h" #include "ash/public/cpp/wallpaper_controller.h"
#include "base/json/json_writer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
...@@ -365,6 +366,33 @@ void ArcIntentHelperBridge::HandleCameraResult( ...@@ -365,6 +366,33 @@ void ArcIntentHelperBridge::HandleCameraResult(
instance->HandleCameraResult(intent_id, action, data, std::move(callback)); instance->HandleCameraResult(intent_id, action, data, std::move(callback));
} }
void ArcIntentHelperBridge::SendNewCaptureBroadcast(bool is_video,
std::string file_path) {
auto* arc_service_manager = arc::ArcServiceManager::Get();
arc::mojom::IntentHelperInstance* instance = nullptr;
if (arc_service_manager) {
instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_service_manager->arc_bridge_service()->intent_helper(),
SendBroadcast);
}
if (!instance) {
LOG(ERROR) << "Failed to get instance for SendBroadcast().";
return;
}
std::string action =
is_video ? "org.chromium.arc.intent_helper.ACTION_SEND_NEW_VIDEO"
: "org.chromium.arc.intent_helper.ACTION_SEND_NEW_PICTURE";
base::DictionaryValue value;
value.SetString("file_path", file_path);
std::string extras;
base::JSONWriter::Write(value, &extras);
instance->SendBroadcast(action, "org.chromium.arc.intent_helper",
/*cls=*/std::string(), extras);
}
// static // static
bool ArcIntentHelperBridge::IsIntentHelperPackage( bool ArcIntentHelperBridge::IsIntentHelperPackage(
const std::string& package_name) { const std::string& package_name) {
......
...@@ -126,6 +126,8 @@ class ArcIntentHelperBridge : public KeyedService, ...@@ -126,6 +126,8 @@ class ArcIntentHelperBridge : public KeyedService,
const std::vector<uint8_t>& data, const std::vector<uint8_t>& data,
arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback); arc::mojom::IntentHelperInstance::HandleCameraResultCallback callback);
void SendNewCaptureBroadcast(bool is_video, std::string file_path);
// Returns false if |package_name| is for the intent_helper apk. // Returns false if |package_name| is for the intent_helper apk.
static bool IsIntentHelperPackage(const std::string& package_name); static bool IsIntentHelperPackage(const std::string& package_name);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment