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() {
}
void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) {
// Check to avoid directory traversal attack.
base::FilePath name_component(name);
if (name_component.ReferencesParent())
base::FilePath path = GetFilePathByName(name);
if (path.empty()) {
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});
apps::mojom::FilePathsPtr launch_files =
apps::mojom::FilePaths::New(file_paths);
apps::AppServiceProxy* proxy =
apps::AppServiceProxyFactory::GetForProfile(profile);
apps::AppServiceProxyFactory::GetForProfile(Profile::FromWebUI(web_ui_));
proxy->LaunchAppWithFiles(
web_app::kMediaAppId,
apps::mojom::LaunchContainer::kLaunchContainerWindow,
......@@ -149,3 +144,32 @@ void ChromeCameraAppUIDelegate::OpenFeedbackDialog(
"chromeos-camera-app" /* category_tag */,
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 {
bool IsMetricsAndCrashReportingEnabled() override;
void OpenFileInGallery(const std::string& name) override;
void OpenFeedbackDialog(const std::string& placeholder) override;
std::string GetFilePathInArcByName(const std::string& name) override;
private:
base::FilePath GetFilePathByName(const std::string& name);
content::WebUI* web_ui_; // Owns |this|.
};
......
......@@ -142,4 +142,9 @@ interface CameraAppHelper {
// Gets the controller to control and monitor the window state of app.
GetWindowStateController()
=> (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) {
CameraAppHelperImpl::CameraAppHelperImpl(
chromeos::CameraAppUI* camera_app_ui,
CameraResultCallback camera_result_callback,
SendBroadcastCallback send_broadcast_callback,
aura::Window* window)
: camera_app_ui_(camera_app_ui),
camera_result_callback_(std::move(camera_result_callback)),
send_broadcast_callback_(std::move(send_broadcast_callback)),
has_external_screen_(HasExternalScreen()),
pending_intent_id_(base::nullopt),
window_(window) {
......@@ -191,6 +193,18 @@ void CameraAppHelperImpl::GetWindowStateController(
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() {
if (tablet_mode_monitor_.is_bound())
tablet_mode_monitor_->Update(true);
......
......@@ -31,6 +31,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
arc::mojom::CameraIntentAction,
const std::vector<uint8_t>&,
HandleCameraResultCallback)>;
using SendBroadcastCallback =
base::RepeatingCallback<void(bool, std::string)>;
using TabletModeMonitor = mojom::TabletModeMonitor;
using ScreenStateMonitor = mojom::ScreenStateMonitor;
using ExternalScreenMonitor = mojom::ExternalScreenMonitor;
......@@ -38,6 +40,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
CameraAppHelperImpl(chromeos::CameraAppUI* camera_app_ui,
CameraResultCallback camera_result_callback,
SendBroadcastCallback send_broadcast_callback,
aura::Window* window);
~CameraAppHelperImpl() override;
void Bind(mojo::PendingReceiver<mojom::CameraAppHelper> receiver);
......@@ -66,6 +69,7 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
SetCameraUsageMonitorCallback callback) override;
void GetWindowStateController(
GetWindowStateControllerCallback callback) override;
void SendNewCaptureBroadcast(bool is_video, const std::string& name) override;
private:
void CheckExternalScreenState();
......@@ -89,6 +93,8 @@ class CameraAppHelperImpl : public ash::TabletModeObserver,
CameraResultCallback camera_result_callback_;
SendBroadcastCallback send_broadcast_callback_;
bool has_external_screen_;
base::Optional<uint32_t> pending_intent_id_;
......
......@@ -122,6 +122,14 @@ void HandleCameraResult(
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>
CreateCameraAppDeviceProvider(const url::Origin& security_origin,
content::BrowserContext* context) {
......@@ -149,9 +157,12 @@ std::unique_ptr<chromeos_camera::CameraAppHelperImpl> CreateCameraAppHelper(
DCHECK_NE(window, nullptr);
auto handle_result_callback =
base::BindRepeating(&HandleCameraResult, browser_context);
auto send_broadcast_callback =
base::BindRepeating(&SendNewCaptureBroadcast, browser_context);
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
......
......@@ -34,6 +34,9 @@ class CameraAppUIDelegate {
// Opens the native chrome feedback dialog scoped to chrome://camera-app and
// show |placeholder| in the description field.
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_
......@@ -13,6 +13,7 @@ import {
// eslint-disable-next-line no-unused-vars
import {ResultSaver} from './models/result_saver.js';
import {VideoSaver} from './models/video_saver.js';
import {ChromeHelper} from './mojo/chrome_helper.js';
import * as util from './util.js';
/**
......@@ -181,6 +182,9 @@ export class GalleryButton {
*/
async savePhoto(blob, name) {
const file = await filesystem.saveBlob(blob, name);
ChromeHelper.getInstance().sendNewCaptureBroadcast(
{isVideo: false, name: file.name});
await this.updateCover_(file);
}
......@@ -198,6 +202,9 @@ export class GalleryButton {
async finishSaveVideo(video) {
const file = await video.endWrite();
assert(file !== null);
ChromeHelper.getInstance().sendNewCaptureBroadcast(
{isVideo: true, name: file.name});
await this.updateCover_(file);
}
}
......@@ -252,6 +252,15 @@ export class ChromeHelper {
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
* exist instance.
......
......@@ -9,6 +9,7 @@
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/wallpaper_controller.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
......@@ -365,6 +366,33 @@ void ArcIntentHelperBridge::HandleCameraResult(
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
bool ArcIntentHelperBridge::IsIntentHelperPackage(
const std::string& package_name) {
......
......@@ -126,6 +126,8 @@ class ArcIntentHelperBridge : public KeyedService,
const std::vector<uint8_t>& data,
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.
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